Android切近实战(一)

简介:

记得上篇文章我还在为使用哪种开发语言开发android而发愁,最近使用了一下C#开发android,感觉不是那么爽。查资料也不方便,于是我决定使用java开发,毕竟java我也是学过的,eclipse环境也不陌生。开始吧,天很冷,先上图

221701997.jpg

这张图我是从真机上截取下来的,这是一个登陆界面。用户输入用户名和密码后,验证,验证通过之后,跳转至另一个Activity。那么我目前采取的方式是Android调用.net WebService的方式。

OK,先看一下Service端。

222008451.jpg
很熟悉很简单的结构。我们简单看一下Service端的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace  GRLC.WebService
{
     /// <summary>
     /// Login 的摘要说明
     /// </summary>
     [WebService(Namespace =  "http://tempuri.org/" )]
     [WebServiceBinding(ConformsTo = WsiProfiles.None)]
     [System.ComponentModel.ToolboxItem( false )]
     // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。
     // [System.Web.Script.Services.ScriptService]
     public  class  LoginService : System.Web.Services.WebService
     {
         [WebMethod]
         public  LoginResponse CheckLogin( string  userNo,  string  pwd)
         {
             return  LoginBiz.GetInstance().CheckLogin(userNo, pwd);
         }
     }
}

没什么好说的,完了之后我们再看Biz层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  class  LoginBiz
    {
        static  LoginBiz loginBiz =  new  LoginBiz();
        private  LoginBiz()
        { }
        public  static  LoginBiz GetInstance()
        {
            return  loginBiz;
        }
        public  LoginResponse CheckLogin( string  name,  string  pwd)
        {
            return  LoginDAL.GetInstance().CheckLogin(name, pwd);
        }
    }

也没啥说的,再看Dal层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public  class  LoginDAL
    {
        static  LoginDAL loginDAL =  new  LoginDAL();
        private  LoginDAL()
        { }
        public  static  LoginDAL GetInstance()
        {
            return  loginDAL;
        }
        const  string  moduleName =  "LoginModule" ;
        BonusEntities bonusEntities =  new  BonusEntities();
        private  string  PwdIsNotCorrect
        {
            get
            {
                return  this .GetMessageByName( "PwdNotCorrect" );
            }
        }
        private  string  UserNotExists
        {
            get
            {
                return  this .GetMessageByName( "UserNotExists" );
            }
        }
        private  string  GetMessageByName( string  msgName)
        {
            return  CommonFunction.GetMessageByModuleAndName(moduleName, msgName);
        }
        public  LoginResponse CheckLogin( string  name,  string  pwd)
        {
            User user = bonusEntities.User.SingleOrDefault(u => u.UseNo == name);
            if  (user !=  null )
            {
                string  passWord = Cryptor.Decrypt(user.Pwd);
                if  (!passWord.Equals(pwd))
                {
                    return  new  LoginResponse() { IsSuccess =  false , FailMsg = PwdIsNotCorrect };
                }
                return  new  LoginResponse() { IsSuccess =  true  };
            }
            else
            {
                return  new  LoginResponse() { IsSuccess =  false , FailMsg = UserNotExists };
            }
        }
    }

在这里我们使用了EntityFrameWork作真正的数据访问层。我一次性把所有表都映射进来了,并设置了他们之间的关联关系。

150001950.png

在这里需要说的是这个GetMessageByModuleAndName方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public  class  CommonFunction
     {
         private  static  string  BaseDirectory
         {
             get
             {
                 return  AppDomain.CurrentDomain.BaseDirectory;
             }
         }
         private  static  List<ServiceMessage> GetMessageList()
         {
             List<ServiceMessage> messageList =  new  List<ServiceMessage>();
             string  messageConfigFolder = Path.Combine(BaseDirectory, ConfigHelper.MessageConfigFolder);
             if  (!Directory.Exists(messageConfigFolder))  return  null ;
             string [] files = Directory.GetFiles(messageConfigFolder,  "*.xml" , SearchOption.TopDirectoryOnly);
             foreach  ( string  file  in  files)
             {
                 ServiceMessage message = SerializeHelper.XmlDeSerializeByFile<ServiceMessage>(file);
                 messageList.Add(message);
             }
             return  messageList;
         }
         public  static  string  GetMessageByModuleAndName( string  moduleName,  string  msgName)
         {
             string  message =  string .Empty;
             ServiceMessage serviceMsg =  new  ServiceMessage();
             List<ServiceMessage> serviceMessageList = CommonFunction.GetMessageList();
             List<MessageEntityCollection> messageEntityCollection = serviceMessageList.Select(m => m.messageEntityCollection).ToList();
             foreach  ( var  msgEntityCollection  in  messageEntityCollection)
             {
                 MessageEntity messageEntity = msgEntityCollection.SingleOrDefault(msgEntity => msgEntity.Module == moduleName && msgEntity.Name == msgName);
                 if  (messageEntity !=  null )
                 {
                     message = messageEntity.Content;
                     break ;
                 }
             }
             return  message;
         }
     }

首先这个方法会先从配置文件里面获取到提示信息的配置文件夹,然后遍历xml文件,将所有xml文件中的提示信息通过反序列化放到一个List中,也就是上面的GetMessageList()方法,其实在这里我们可以对代码进行优化,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private  static  List<ServiceMessage> _serviceMessageList;
         private  static  List<ServiceMessage> ServiceMessageList
         {
             get
             {
                 if  (_serviceMessageList ==  null )
                 {
                     _serviceMessageList = GetMessageList();
                     return  _serviceMessageList;
                 }
                 else
                 {
                     return  _serviceMessageList;
                 }
             }
         }

那么每次在获取的时候,会先判断_serviceMessageList有没有,如果有,就不再读取文件了。

我们来看一下这个配置文件

1
2
3
4
5
<? xml  version = "1.0"  encoding = "utf-8" ?>
< ServiceMessages >
   < Message  Module = "LoginModule"  Name = "PwdNotCorrect"  Content = "密码不正确!" ></ Message >
   < Message  Module = "LoginModule"  Name = "UserNotExists"  Content = "用户名不存在!" ></ Message >
</ ServiceMessages >

它对应的实体如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace  GRLC.Model.Config
{
     [XmlRoot( "ServiceMessages" )]
     public  class  ServiceMessage
     {
         [XmlElement( "Message" )]
         public  MessageEntityCollection messageEntityCollection {  get set ; }
     }
     public  class  MessageEntityCollection : KeyedCollection< string , MessageEntity>
     {
         protected  override  string  GetKeyForItem(MessageEntity messageEntity)
         {
             return  messageEntity.Name;
         }
     }
     [XmlRoot( "Message" )]
     public  class  MessageEntity
     {
         [XmlAttribute( "Name" )]
         public  string  Name {  get set ; }
         [XmlAttribute( "Module" )]
         public  string  Module {  get set ; }
         [XmlAttribute( "Content" )]
         public  string  Content {  get set ; }
     }
}

所以在拿到这些msg以后,根据他的module和name就可以拿到对应content。好了上面就是.net的service端。完了之后我们部署一下

230437715.jpg

部署的时候需要注意如下

230534281.jpg

230552505.jpg


接下来我们看一下app端的开发

224421241.jpg

我们总共有两个Activity,一个是main,一个是index,main界面登成功跳转至index界面。

我们先看一下mai界面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<? xml  version = "1.0"  encoding = "utf-8" ?>
< LinearLayout  xmlns:android = "http://schemas.android.com/apk/res/android"
     android:id = "@+id/layMain"
     android:layout_width = "fill_parent"  android:layout_height = "fill_parent"
     android:paddingLeft = "10dp"  android:paddingRight = "10dp"
     android:orientation = "vertical"
     android:gravity = "center_vertical" >
     < LinearLayout  android:orientation = "horizontal"
         android:gravity = "center_vertical"  android:layout_width = "fill_parent"
         android:layout_height = "wrap_content" >
         < TextView  android:id = "@+id/labUserName"
             android:text = "@string/labUserName"
             android:layout_width = "70dp"
             android:layout_height = "wrap_content"
             android:textSize = "18dp"
             android:textStyle = "bold" ></ TextView >
         < EditText  android:id = "@+id/txtUserName"
             android:hint = "@string/hintInputUserNo"
             android:layout_width = "fill_parent"
             android:layout_height = "wrap_content"
             android:singleLine = "true" ></ EditText >
     </ LinearLayout >
     < LinearLayout  android:orientation = "horizontal"
         android:layout_width = "fill_parent"  android:layout_height = "wrap_content" >
         < TextView  android:id = "@+id/labPwd"
             android:text = "@string/labPwd"
             android:layout_width = "70dp"
             android:layout_height = "wrap_content"
             android:textSize = "18dp"
             android:textStyle = "bold" ></ TextView >
         < EditText  android:id = "@+id/txtPwd"
             android:hint = "@string/hintInputUserPwd"
             android:layout_height = "wrap_content"
             android:layout_width = "fill_parent"
             android:singleLine = "true"
             android:password = "true" ></ EditText >
     </ LinearLayout >
     < LinearLayout  android:orientation = "horizontal"  android:layout_width = "fill_parent"
         android:gravity = "center_horizontal"  android:layout_height = "wrap_content" >
         < CheckBox  android:id = "@+id/chkDisplayPwd"
         android:layout_width = "130dp"
         android:layout_height = "wrap_content"
         android:text = "@string/chkDisplayPwd" >
         </ CheckBox >
         < Button  android:id = "@+id/btnLogin"  android:text = "@string/btnLoginText"
             android:layout_width = "80dp"  android:layout_height = "wrap_content" ></ Button >
             < Button  android:id = "@+id/btnCancel"  android:text = "@string/btnCancelText"
             android:layout_width = "80dp"  android:layout_height = "wrap_content" ></ Button >
     </ LinearLayout >
</ LinearLayout >

采用的是线性布局。运行起来就是最开始我贴的那张图。OK,我们先看一下OnCreate


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
final  static  String NAMESPACE =  "http://tempuri.org/" ;
     final  static  String METHOD_NAME =  "CheckLogin" ;
     final  static  String SOAP_ACTION =  "http://tempuri.org/CheckLogin" ;
     final  static  String URL =  "http://10.0.2.2:2000/LoginService.asmx?wsdl" ;
     EditText txtUserno;
     EditText txtPwd;
     Button btnLogin;
     Button btnCancel;
     CheckBox chkDisplay;
     /** Called when the activity is first created. */
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.main);
         btnLogin = (Button)  this .findViewById(R.id.btnLogin);
         btnCancel=(Button)  this .findViewById(R.id.btnCancel);
         txtUserno = (EditText)  this .findViewById(R.id.txtUserName);
         txtPwd = (EditText)  this .findViewById(R.id.txtPwd);
         chkDisplay = (CheckBox)  this .findViewById(R.id.chkDisplayPwd);
         btnLogin.setOnClickListener( new  OnClickListener() {
             public  void  onClick(View v) {
                 String userNo = txtUserno.getText().toString().trim();
                 String pwd = txtPwd.getText().toString().trim();
                 Login(userNo, pwd);
             }
         });
         btnCancel.setOnClickListener( new  OnClickListener() {
             public  void  onClick(View v) {
                 txtUserno.setText( "" );
                 txtPwd.setText( "" );
             }
         });
         chkDisplay.setOnCheckedChangeListener( new  OnCheckedChangeListener() {
             public  void  onCheckedChanged(CompoundButton cmpButton,
                     boolean  isChecked) {
                 if  (isChecked) {
                     txtPwd
                             .setTransformationMethod(HideReturnsTransformationMethod
                                     .getInstance());
                 else  {
                     txtPwd.setTransformationMethod(PasswordTransformationMethod
                             .getInstance());
                 }
             }
         });
     }

在页面的最顶端声明了我们需要调用的webservice的信息,注意10.0.2.2是android的内置IP,这个是android访问你本地电脑时使用的IP。如果使用127.0.0.1的话那是在访问你的手机。在Oncreate方法里,我们给按钮注册了事件响应。登录和取消,我们还看到一个checkBox,他的作用是是否让密码明文显示。如果未勾选,如下左所示,否则如右

230208396.jpg230309988.jpg

OK,看完checkBox我们看登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public  void  Login(String userNo, String passWord) {
         if  (!CheckUserInput(userNo, passWord)) {
             return ;
         }
         SoapObject response =  this .GetServerResponse(userNo, passWord);
         boolean  isSuccess = Boolean.valueOf(response.getProperty( "IsSuccess" )
                 .toString());
         if  (!isSuccess) {
             String failMsg = response.getProperty( "FailMsg" ).toString();
             new  AlertDialog.Builder( this )
                     .setTitle(R.string.WarningMsgTitle)
                     .setMessage(failMsg)
                     .setIcon(R.drawable.warning)
                     .setPositiveButton( "确定" , null ).show();
         else  {
             Intent intent =  new  Intent();
             Bundle bundle =  new  Bundle();
             bundle.putString( "userNo" , userNo);
             intent.putExtras(bundle);
             intent.setClass(main. this , index. class );
             startActivityForResult(intent,  0 );
             finish();
         }
     }

登录的时候会先checkInput。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private  boolean  CheckUserInput(String userNo, String passWord) {
         if  (userNo.equals( "" )) {
             new  AlertDialog.Builder( this ).setTitle(R.string.WarningMsgTitle)
                     .setMessage(R.string.UserNoIsEmpty)
                     .setIcon(R.drawable.info)
                     .setPositiveButton( "确定" ,
                             new  DialogInterface.OnClickListener() {
                                 public  void  onClick(
                                         DialogInterface dialoginterface,  int  i) {
                                     txtUserno.requestFocus();
                                 }
                             }).show();
             return  false ;
         }
         if  (passWord.equals( "" )) {
             new  AlertDialog.Builder( this ).setTitle(R.string.WarningMsgTitle)
                     .setMessage(R.string.UserPWdIsEmpty)
                     .setIcon(R.drawable.info)
                     .setPositiveButton(
                             "确定" new  DialogInterface.OnClickListener() {
                                 public  void  onClick(
                                         DialogInterface dialoginterface,  int  i) {
                                     txtPwd.requestFocus();
                                 }
                             }).show();
             return  false ;
         }
         return  true ;
     }

checkInput非常简单,如果是空的话弹出提示,如下

231011783.jpg231029559.jpg

OK,如果输入的不是空,那么调用WebService,需要传递两个参数

231438392.jpg

看一下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private  SoapObject GetServerResponse(String userNo, String passWord) {
         SoapObject Request =  new  SoapObject(NAMESPACE, METHOD_NAME);
         PropertyInfo pi =  new  PropertyInfo();
         pi.setName( "userNo" );
         pi.setType(String. class );
         pi.setValue(userNo);
         Request.addProperty(pi);
         pi =  new  PropertyInfo();
         pi.setName( "pwd" );
         pi.setType(String. class );
         pi.setValue(passWord);
         Request.addProperty(pi);
         SoapSerializationEnvelope soapEnvelope =  new  SoapSerializationEnvelope(
                 SoapEnvelope.VER11);
         soapEnvelope.dotNet =  true ;
         soapEnvelope.setOutputSoapObject(Request);
         HttpTransportSE httpTS =  new  HttpTransportSE(URL);
         try  {
             httpTS.call(SOAP_ACTION, soapEnvelope);
         catch  (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         catch  (XmlPullParserException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         SoapObject result =  null ;
         try  {
             result = (SoapObject) soapEnvelope.getResponse();
         catch  (SoapFault e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         return  result;
     }

在这里调用WebService并返回SoapObject对象,调用WebService不要忘记引用ksoap

231730297.jpg

如果没有的话,自己去下载一个。这里调用成功返回以后,如果IsSuccess为true的话,直接跳转至index,并将登陆成功的userNo传给index。如果登陆失败,弹出service端返回的提示信息。但悲剧的是我的真机报错,没有访问到webservice。后来我用手机连接了wlan,Ip地址也改了还是报错

000058489.jpg

电脑访问无线IP没有问题,如下

000215771.jpg

但是真机就是连不上,模拟器用10.0.2.2却可以正常访问。

150223923.png

后来查看了一下手机,尝试下面所有的ip都不行。

152147922.png

最后我查了一下电脑ip,使用172.18.73.39,结果调用成功。

152347314.png

调用成功了,哈哈

152515547.png 152849640.png



本文转自 BruceAndLee 51CTO博客,原文链接:http://blog.51cto.com/leelei/1345860,如需转载请自行联系原作者


相关文章
|
4天前
|
设计模式 Android开发 Kotlin
Android经典实战之Kotlin委托模式和by关键字
本文介绍了Kotlin中`by`关键字在类及属性委托中的运用,通过实例展示了如何利用类委托简化接口实现,以及如何借助标准与自定义属性委托管理属性的读写操作。通过`by`关键字的支持,Kotlin使得委托模式的实现更为直观且高效。
20 4
|
4天前
|
缓存 安全 Android开发
Android经典实战之用Kotlin泛型实现键值对缓存
本文介绍了Kotlin中泛型的基础知识与实际应用。泛型能提升代码的重用性、类型安全及可读性。文中详细解释了泛型的基本语法、泛型函数、泛型约束以及协变和逆变的概念,并通过一个数据缓存系统的实例展示了泛型的强大功能。
14 2
|
15天前
|
缓存 数据处理 Android开发
Android经典实战之Kotlin常用的 Flow 操作符
本文介绍 Kotlin 中 `Flow` 的多种实用操作符,包括转换、过滤、聚合等,通过简洁易懂的例子展示了每个操作符的功能,如 `map`、`filter` 和 `fold` 等,帮助开发者更好地理解和运用 `Flow` 来处理异步数据流。
62 4
|
12天前
|
编译器 API Android开发
Android经典实战之Kotlin Multiplatform 中,如何处理不同平台的 API 调用
本文介绍Kotlin Multiplatform (KMP) 中使用 `expect` 和 `actual` 关键字处理多平台API调用的方法。通过共通代码集定义预期API,各平台提供具体实现,编译器确保正确匹配,支持依赖注入、枚举类处理等,实现跨平台代码重用与原生性能。附带示例展示如何定义跨平台函数与类。
42 0
|
8天前
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
36 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
1天前
|
监控 Java API
Android经典实战之OkDownload:一个经典强大的文件下载开源库,支持断点续传
本文介绍的 OkDownload 是一个专为 Android 设计的开源下载框架,支持多线程下载、断点续传和任务队列管理等功能,具备可靠性、灵活性和高性能特点。它提供了多种配置选项和监听器,便于开发者集成和扩展。尽管已多年未更新,但依然适用于大多数文件下载需求。
10 1
|
2天前
|
算法 安全 数据安全/隐私保护
Android经典实战之常见的移动端加密算法和用kotlin进行AES-256加密和解密
本文介绍了移动端开发中常用的数据加密算法,包括对称加密(如 AES 和 DES)、非对称加密(如 RSA)、散列算法(如 SHA-256 和 MD5)及消息认证码(如 HMAC)。重点讲解了如何使用 Kotlin 实现 AES-256 的加密和解密,并提供了详细的代码示例。通过生成密钥、加密和解密数据等步骤,展示了如何在 Kotlin 项目中实现数据的安全加密。
29 1
|
2天前
|
存储 安全 API
Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
15 1
|
3天前
|
算法 安全 数据安全/隐私保护
Android经典实战之常见的移动端加密算法和用kotlin进行AES-256加密和解密
本文介绍了移动端开发中常用的数据加密算法,包括对称加密(如 AES 和 DES)、非对称加密(如 RSA)、散列算法(如 SHA-256 和 MD5)及消息认证码(如 HMAC)。重点展示了如何使用 Kotlin 实现 AES-256 的加密和解密,提供了详细的代码示例。
12 2
|
3天前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
16 1