Android开发注意事项(4-6)

注意事项:

  1. 权限申请

普通权限申请:
  对于普通权限,即那些不会直接威胁到用户的安全和隐私的权限,对于这部分权限申请,在AndroidManifest中文件写入即可。例如:

 




运行时权限申请:
  对于运行时权限申请,必须要由用户手动点击授权才可以。相关权限都是危险权限,即那些可能会触及用户隐私或者对设备安全性造成影响的权限。如获取设备联系人信息、地理位置等。下面以CALL_PHONE这个权限为例

if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)
 != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.CALL_PHONE}, 1);
else{
XXXXXXXX
}

public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
      case 1:
              if (grantResults.length > 0 && grantResults[0] == packageManager.PERMISSION_GRANTED) {
                                        XXXXXXXXX;
                   }

          }
}

  第一步先要判断用户是不是已经授权了,借助的是ContextCompat.checkSelfPermission()方法。checkSelfPermission()方法接受两个参数,第一个参数是Context,第二个参数是具体的权限名。然后使用方法的返回值和PackageManager.PERMISSION_GRANTED做比较,相等就说明用户已经授权,不等就表示用户没有授权。
  如果没有授权的话,则需要调用ActivityCompat.requestPermissions()方法来向用户申请授权。requestPermission()方法接收3个参数,第一个参数要求是Activity的实例,第二个参数是一个string数组,用来放申请的权限名,第三个参数是请求码。
  之后,系统会弹出一个权限申请的对话框,然后用户同意或拒接权限申请。最后会回调onRequestPermissionResult()方法,授权的结果封装在grantResults参数中。

  1. 调试与测试
    调试
  1. 记录栈跟踪的诊断性日志
      例:检查doTest()方法是否被调用,可以使用Log.d(String, String, Throwable)。此方法记录并输出整个栈跟踪日志。可以很容易看出doTest()方法在哪些地方被调用了
      作为参数传入Log.d(String, String, Throwable)方法的异常,不一定就是已捕获的抛出异常。可以创建一个全新的Exception,把它作为不抛出的异常对象传入。例:Log.d("TAG", "TAG", new Exception())。
  1. 利用调试器设置断点调试
      使用AndroidStudio自带的调试器调试doTest()方法,首先要在刚方法内设置断点以确认该方法是否被调用。断点会在断点设置行的前一行停止代码执行。
      设置好断点以后,单击Debug按钮,启动调试。
    断点调试.JPG
    调试视图.JPG

      在调试视图中,左半部分为栈列表,可查看方法的调用顺序右半部分为变量视图,可以让观察程序中各个变量的值
      通过单击单步执行按钮,继续执行代码,查看代码更深层次的调用(源码内的方法调用),观察变量的值,直至找出问题的关键。当想跳过查看某一行代码的执行细节,可用单步执行跳过按钮,跳过此行代码;当想跳出某个方法的执行细节,可点击单步执行跳出,跳出此方法。

测试

单元测试

整合测试

  1. 设计模式

MVC设计模式
  M:模型、V:视图(布局)、C:控制器
  一些Android应用基于M-V-C架构模式(即模型-视图-控制器的架构模式)进行设计。MVC模式表明,应用的任何对象,归根结底都属于模型对象、视图对象以及控制器对象中的一种。
  模型对象:存储着应用的数据,模型类通常用来映射与应用相关的一些事物。模型对象不关心用户界面,它为存储和管理应用数据而生
  视图对象在屏幕上绘制,以及响应用户的输入。凡是在屏幕上能看见的对象,就是视图对象。
  控制器对象:含有应用的逻辑单元,是视图对象与模型对象的联系纽带。控制器对象响应视图对象触发的各类事件,此外还管理着模型对象与视图层间的数据流动

mvc架构模式.jpg

  在响应诸如单击按钮等用户事件时,对象间的交互控制着数据流。模型对象与视图对象不直接交互。控制器作为他们之间的联系纽带,接收对象发送的消息,然后向其他对象发送操作指令。

MVC数据控制流与用户交互.jpg

MVC设计模式的好处
  随着应用功能的持续扩展,应用往往会变得过于复杂。把java类以模型层、视图层、控制器层进行分类组织,我们就可以按层而非一个个类来考虑设计开发了。例如想要升级视图层,在界面上添加一个按钮,就不需要考虑模型类。
  MVC的设计模式还便于复用类。相比功能多而全的类,功能单一的专用类更有利于代码复用。
MVC设计模式的缺点:
  1. MVC的真实存在是MC(V),Model和Controller根本没办法分开,并且数据和View严重耦合,也导致View也包含了业务逻辑。
  2. Controller会变得很厚很复杂。

MVP设计模式
  M:模型、V:视图(布局)、P:表现者。MVC的演变模式,将控制器(Controller)变为Presenter(表现者)。目的在于将模型层(Model)与视图层(View)解耦。,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。

mvp架构模式.jpg

  如上图所示,MVP中将MVC中原有的V,拆分为V+P,并将View层实现接口化。这样View层彻底与数据和业务逻辑分离,View层只关心和用户的交互。即用户进行某种操作,View层只负责提供响应接口具体的业务实现和数据操作都交由Presenter(Presenter必须要拿到View和Model的实例)。

MVP模式实例:

Model层:
/** 
定义业务接口 
*/ 
public interface IUserBiz { 
    public void login(String username, String password, OnLoginListener loginListener); 
} 
/** 
结果回调接口 
*/ 
public interface OnLoginListener { 
    void loginSuccess(User user); 
    void loginFailed(); 
} 
/** 
具体Model的实现 
*/ 
public class UserBiz implements IUserBiz { 
    @Override 
    public void login(final String username, final String password, final OnLoginListener loginListener) 
    { 
        //模拟子线程耗时操作 
        new Thread() 
        { 
            @Override 
            public void run() 
            { 
                try 
                { 
                    Thread.sleep(2000); 
                } catch (InterruptedException e) 
                { 
                    e.printStackTrace(); 
                } 
                //模拟登录成功 
                if ("zhy".equals(username) && "123".equals(password)) 
                { 
                    User user = new User(); 
                    user.setUsername(username); 
                    user.setPassword(password); 
                    loginListener.loginSuccess(user); 
                } else 
                { 
                    loginListener.loginFailed(); 
                } 
            } 
        }.start(); 
    } 
}
View层:
public interface IUserLoginView  {  
    String getUserName();  
    String getPassword();  
    void clearUserName();  
    void clearPassword();  
    void showLoading();  
    void hideLoading();  
    void toMainActivity(User user);  
    void showFailedError();  
}

Activity实现View接口:
public class UserLoginActivity extends ActionBarActivity implements IUserLoginView { 
    private EditText mEtUsername, mEtPassword; 
    private Button mBtnLogin, mBtnClear; 
    private ProgressBar mPbLoading; 
    private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this); 
    @Override 
    protected void onCreate(Bundle savedInstanceState) 
    { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_user_login); 
        initViews(); 
    } 
    private void initViews() 
    { 
        mEtUsername = (EditText) findViewById(R.id.id_et_username); 
        mEtPassword = (EditText) findViewById(R.id.id_et_password); 
        mBtnClear = (Button) findViewById(R.id.id_btn_clear); 
        mBtnLogin = (Button) findViewById(R.id.id_btn_login); 
        mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading); 
        mBtnLogin.setOnClickListener(new View.OnClickListener() 
        { 
            @Override 
            public void onClick(View v) 
            { 
                mUserLoginPresenter.login(); 
            } 
        }); 
        mBtnClear.setOnClickListener(new View.OnClickListener() 
        { 
            @Override 
            public void onClick(View v) 
            { 
                mUserLoginPresenter.clear(); 
            } 
        }); 
    } 
    @Override 
    public String getUserName() 
    { 
        return mEtUsername.getText().toString(); 
    } 
    @Override 
    public String getPassword() 
    { 
        return mEtPassword.getText().toString(); 
    } 
    @Override 
    public void clearUserName() 
    { 
        mEtUsername.setText(""); 
    } 
    @Override 
    public void clearPassword() 
    { 
        mEtPassword.setText(""); 
    } 
    @Override 
    public void showLoading() 
    { 
        mPbLoading.setVisibility(View.VISIBLE); 
    } 
    @Override 
    public void hideLoading() 
    { 
        mPbLoading.setVisibility(View.GONE); 
    } 
    @Override 
    public void toMainActivity(User user) 
    { 
        Toast.makeText(this, user.getUsername() + 
                " login success , to MainActivity", Toast.LENGTH_SHORT).show(); 
    } 
    @Override 
    public void showFailedError() 
    { 
        Toast.makeText(this, 
                "login failed", Toast.LENGTH_SHORT).show(); 
    } 
}
Presenter层:
public class UserLoginPresenter { 
    private IUserBiz userBiz; 
    private IUserLoginView userLoginView; 
    private Handler mHandler = new Handler(); 
//Presenter必须要能拿到View和Model的实现类 
    public UserLoginPresenter(IUserLoginView userLoginView) 
    { 
        this.userLoginView = userLoginView; 
        this.userBiz = new UserBiz(); 
    } 
    public void login() 
    { 
        userLoginView.showLoading(); 
        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() 
        { 
            @Override 
            public void loginSuccess(final User user) 
            { 
                //需要在UI线程执行 
                mHandler.post(new Runnable() 
                { 
                    @Override 
                    public void run() 
                    { 
                        userLoginView.toMainActivity(user); 
                        userLoginView.hideLoading(); 
                    } 
                }); 
            } 
            @Override 
            public void loginFailed() 
            { 
                //需要在UI线程执行 
                mHandler.post(new Runnable() 
                { 
                    @Override 
                    public void run() 
                    { 
                        userLoginView.showFailedError(); 
                        userLoginView.hideLoading(); 
                    } 
                }); 
            } 
        }); 
    } 
    public void clear() 
    { 
        userLoginView.clearUserName(); 
        userLoginView.clearPassword(); 
    } 
}

MVVM设计模式:
  M:模型、V:视图(布局)、VM:视图模型。MVP模式的改进版,MVVM架构很好地把控制器(表现者)里的臃肿代码抽到布局文件里,让开发人员很容易看出哪些时动态界面。同时,它也抽出部分动态控制器代码放入ViewModel类,大大方便了开发测试和验证。

操作步骤:

  1. 启用数据绑定:
app/build.gradle:

 buildTypes {
        release {
         .......
        }
    }
    
    dataBinding{
        enabled = true
    }

dataBinding{enabled = true},会打开IDE的整合功能允许使用数据绑定产生的类,并把他们整合到编译里去。

  1. 将一般布局改造为数据绑定布局:
      要在布局里使用数据绑定,首先要把一般布局改造为数据绑定布局。具体做法就是把整个布局定义放入标签。之后,数据绑定工具会帮助生成一个绑定类。新产生的绑定类默认以布局文件命名
fragment_test.xml



    


  1. 实例化绑定类
      现在,activity_main.xml已经有了一个叫FragmentTestBinding的绑定类。实例化视图层级结构时,不要再使用LayoutInflater,而是使用DataBindingUtil实例化FragmentTestBinding类。FragmentTest类有两个引用:getRoot()和recyclerView,前者指整个布局,后者指RecyclerView
@Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        FragmentTestBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_test,
                container, false);
        
        binding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        return binding.getRoot();
    }

  以上就是简单的数据绑定,不用findViewById()方法,转而使用数据绑定获取视图

  1. 创建、整合视图模型(ViewModel)
      首先,创建视图模型ViewModel:
public class TestViewModel {

    //Model层
    private TestModel mTestModel;

    public TestViewModel(TestModel testModel) {
        mTestModel = testModel;
    }

    public TestModel getTestModel() {
        return mTestModel;
    }
    
    ........

}

  然后,把视图模型整合到布局文件里:



    
        
    

    

        

  最后,关联使用视图模型:

@Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        FragmentTestBinding binding = DataBindingUtil.inflate(inflater, R.layout.activity_main,
                container, false);

         //关联使用视图模型
        binding.setViewModel(new TestViewModel(new TestModel()));
         //获取视图模型,并进行更新等操作
        binding.getViewModel().setSomeThing();
        binding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        return binding.getRoot();
    }
  1. 绑定数据观察
      当我们更新Model层的数据时,View层并不知情ViewModel层需要实现数据绑定的BaseObservable接口。这个接口可以让绑定类在ViewModel上设置监听器,只要视图模型有变化,绑定类立即会接到回调。
public class TestViewModel extends BaseObservable {
    //Model层
    private TestModel mTestModel;

    public TestViewModel(TestModel testModel) {
        mTestModel = testModel;
    }

    public void setSomeThing() {
      
          .......
        //通知绑定类,视图模型上所有可绑定的属性已经更新
      notifyChange();  
      //通知绑定类,视图模型上只有用@Bindable注解的方法值有变
      notifyPropertyChanged(com.futuring.renrenslidinglayout.BR.testModel);
    }

     //注解视图模型中可绑定的属性,当属性更新时,重新调用此方法
    @Bindable
    public TestModel getTestModel() {
        return mTestModel;
    }

    public void onClick() {

    }

    public void onButtonClick(){

    }
}

  当ViewModel中的Model信息更新,调用notifyChange()通知绑定类,在View层中更新所有可绑定的属性。调用notifyPropertyChanged(int BR.),通知绑定类,在View层中只更新与@Bindable注解的方法有关的值即可

你可能感兴趣的:(Android开发注意事项(4-6))