首页 > 技术文章 > 每日一点点Android的积攒

liunx1109 2019-06-19 11:11 原文

一、Broadcast广播的学习

  1.Broadcast种类

   a.普通广播 :  通过sendBroadcast进行发送,如果注册了Action匹配的接受者则会收到,若发送广播有相应权限,那么广播接收者也需要相应权限

   b.系统广播 :  

   c.有序广播(Ordered Broadcast :  通过sendOrderedBroadcast发送,广播接受者接收广播的顺序规则:Priority大的优先,动态注册的接收者优先,先接收的可以对广播进行截断和修改

   d.App应用内广播(本地广播、Local Broadcast):  通过LocalBroadcastManager.getInstance(this).sendBroadcastSync(),App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App

                    相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高(本地广播只会在APP内传播,安全性高;不允许其他APP对自己的APP发送广播,效率高

   e.粘性广播(Sticky Broadcast)

  2.广播的注册方式

   a.静态注册 :  在AndroidManifest.xml中注册的广播 , 当前应用关闭了,还是可以收到广播的

   b.动态注册 :  在Service或者Activity组件中,通过Context.registerReceiver()注册广播接收器,它的生命周期与组件一致

  3. localBroadcastManager的实现机制

   它内部是通过Handler实现的,那么相比与系统广播通过Binder实现那肯定是更高效了,同时使用Handler来实现,别的应用无法向我们的应用发送该广播,而我们应用内发送的广播也不会离开我们的应用

   LocalBroadcastManager内部协作主要是靠这两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要就是存储待接收的广播对象.

  

  二、handler的进一步学习

    1.为什么在子线程中创建Handler会抛异常?

     Handler的工作是依赖于Looper的,而Looper(与消息队列)又是属于某一个线程,其他线程不能访问, 因此Handler就是间接跟线程是绑定在一起了。因此要使用Handler必须要保证Handler所创建的线程中有Looper对象并且启动循环。因为子线程中默认是没有Looper的,所以会报错。   主线程中默认是创建了Looper并且启动了消息的循环的,因此不会报错。

    2.为什么不能在子线程更新UI?

     UI更新的时候,会对当前线程进行检验,如果不是主线程,则抛出异常.

    在三种特殊情况下是可以子线程更新UI的

    a.在Activity创建完成后,mThread被赋值为主线程(ViewRootImpl),所以直接在onCreate中创建子线程是可以更新UI的 

    b.在子线程中添加 Window,并且创建 ViewRootImpl,可以在子线程中更新view

    c.SurfaceView可以在其他线程更新  

  

  三、Android Framework的源码分析

    1.AMS与APP、Activity的启动流程

     

  四、AsyncTask的学习

    class GetMeiNvLogoTaskextends AsyncTask<String,Integer,Bitmap> { //继承AsyncTask
        /**
         * 其中的三个参数
         * String:指的是doInBackground()方法中的参数类型  即第一个参数
         * Integer:指的是onProgressUpdate()方法中的参数类型 即第二个参数
         * Bitmap:指的是onPostExecute()的方法中的参数类型和doInBackground()方法的返回值类型 即第三个参数
         */
        //处理后台执行的任务,在后台线程执行,主要用于进行异步操作
        @Override
        protected Bitmap doInBackground(String... params) {
            //用于发布更新消息,每次执行引方法都将会调用onProgressUpdate(Integer... progress)方法
            publishProgress(0);
            HttpClient hc = new DefaultHttpClient();
            //用于发布更新消息,每次执行到此方法都会调用onProgressUpdate(Integer... progress)方法
            publishProgress(10);
            HttpGet hg = new HttpGet(params[0]);//获取csdn的logo
            //定义一个Bitmap
            final Bitmap bm;
            try {
                HttpResponse hr = hc.execute(hg);
    bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
            } catch (Exception e) {
                return null;
            }
            publishProgress(100);
            //mImageView.setImageBitmap(result); 不能在后台线程操作ui
            return bm; //返回的值会传入到onPostExecute()方法的参数中
        }
        
        //在doInBackground方法中每次执行publishProgress之后此方法都会被调用,在ui线程执行
        //用于在执行异步任务的过程中,对用户进行提示,例如:控制进度条等。
        protected void onProgressUpdate(Integer... progress) {
            mProgressBar.setProgress(progress[0]);//更新进度条的进度
         }
        
        //后台任务(doInBackground())执行完之后被调用,在ui线程执行,
        //主要用于将异步任务执行的结果返回给客户
         protected void onPostExecute(Bitmap result) {
             if(result != null) {
                 Toast.makeText(AsyncTaskActivity.this, "成功获取图片", Toast.LENGTH_LONG).show();
                 //把下载下来的图片设置在ImageView上
                 mImageView.setImageBitmap(result);
             }else {
                 Toast.makeText(AsyncTaskActivity.this, "获取图片失败", Toast.LENGTH_LONG).show();
             }
         }
         
       //在 doInBackground(Params...)之前,execute()方法之后被调用,在ui线程执行,所以可以设置控件大小属性等。
            //主要用于异步操作之前的UI的准备工作
         protected void onPreExecute () {
             mImageView.setImageBitmap(null);
             mProgressBar.setProgress(0);//进度条复位
         }
         
         protected void onCancelled () {//在ui线程执行
             mProgressBar.setProgress(0);//进度条复位
         }
    }
  ##### 三个参数

  * Params:表示后台任务执行时的参数类型,该参数会传给AysncTask的doInBackground()方法

   * Progress:表示后台任务的执行进度的参数类型,该参数会作为onProgressUpdate()方法的参数

   * Result:表示后台任务的返回结果的参数类型,该参数会作为onPostExecute()方法的参数  

  

  ##### 五个方法

  * onPreExecute():异步任务开启之前回调,在主线程中执行

   * doInBackground():执行异步任务,在线程池中执行

  * onProgressUpdate():当doInBackground中调用publishProgress时回调,在主线程中执行

  * onPostExecute():在异步任务执行之后回调,在主线程中执行

  * onCancelled():在异步任务被取消时回调

 五、UI卡顿问题的处理

   1.布局优化

    a.include复用布局、使用ViewStub延迟加载布局、使用merge减少代码层级、使用RelativeLayout也能大大减少视图的层级、慎重设置整体背景颜色防止过度绘制

    b.使用自定义View取代复杂的View

   2.在UI线程中做轻微的耗时操作,导致UI线程卡顿:应该把耗时操作放在子线程中进行,UI卡顿最严重的后果是ANR,因此需要在开发中避免和解决ANR问题

   3.列表控件滑动卡顿:复用convertView、滑动不进行加载、使用压缩图片、加载缩略图等。 

  六、Bitmap的理解和学习

    1.Bitmap的内存占用大小与三个因素有关:

     a.色彩格式,前面我们已经提到,如果是ARGB_8888那么就是一个像素4个字节,如果是RGB_565那就是2个字节

     b.原始文件存放的资源目录(分辨率越小,内存占用越小)

     c.目标屏幕的密度(屏幕的密度越小,内存占用越小)

    2.Bitmap的色彩格式

     a.ARGB_8888的内存消耗是RGB_565的2倍

     b.ARGB_8888格式比RGB_565多了一个透明通道

     c.如果使用RGB_565格式解析ARGB_8888格式的图片(png),可能会导致图片变绿

    3.Bitmap的回收

     Android8.0之后Bitmap的像素数据是存放Native内存中,我们需要回收Native层和Java层的内存,使用recycle方法进行回收,

     该方法也可以不主动调用,因为垃圾回收器会自动收集不可用的Bitmap对象进行回收,recycle方法会判断Bitmap在不可用的情况下,将发送指令到垃圾回收器,让其回收native层和Java层的内存,则Bitmap进入dead状态

    

二、Fragment的学习

  1. fragment的生命周期

   onCreate  onStart  onResume onPaused onStoped onDestory onRestart  ---------->上面为Activity的生命周期

   onAttach  onCreateView  onActivityCreated  onDestoryView onDetach

   

  2.Fragment创建/加载到Activity的两种方式

   a.静态加载的方式

    1. 创建Fragment的xml布局文件

    2. 在Fragment的onCreateView中inflate布局,返回

    3. 在Activity的布局文件中的适当位置添加fragment标签,指定name为Fragment的完整类名(这时候Activity中可以直接通过findViewById找到Fragment中的控件)

   b.动态加载

    1. 创建Fragment的xml布局文件

    2. 在Fragment的onCreateView中inflate布局,返回

    ```java @Nullable @Override

    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)

    {

      return inflater.inflate(R.layout.activity_main, container, false); }  

    3. 在Activity中通过获取FragmentManager(SupportFragmentManager),通过beginTransaction()方法开启事务

    4. 进行add()/remove()/replace()/attach()/detach()/hide()/addToBackStack()事务操作(都是对Fragment的栈进行操作,其中add()指定的tag参数可以方便以后通过findFragmentByTag()找到这个Fragment)

    5. 提交事务:commit()

    示例代码: ```java @Override

    protected void onCreate(Bundle savedInstanceState)

    {

    super.onCreate(savedInstanceState);

     setContentView(R.layout.activity_main);

    getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, new TestFragment(), "test") .commit();

    TestFragment f = (TestFragment) getSupportFragmentManager().findFragmentByTag("test");   -------------------------->通过找标签的形式找到fragment这个对象

   }

  3.fragment的之间通信

   a.Fragment之间直接通信

   public void onClick(View v) {

    Fragment fragment = CrimeFragment.newInstance(mCrime.getId());

    FragmentManager fm = getActivity().getSupportFragmentManager();

    fm.beginTransaction() .add(R.id.detail_fragment_container, fragment) .commit();

  }

  Fragment.getActivity().getSupportFragmentManager()调用获取到其托管activity的FragmentManager,然后直接通过FragmentManager替换掉右侧的Fragment
  缺点 : 复用性太差,不能保证fragment的独立性

  b.通过Activity使用Fragment回调接口(推荐)

  public class CrimeListFragment extends Fragment {

  ...

  private boolean mSubtitleVisible;

  private Callbacks mCallbacks;

  /** * Required interface for hosting activities. */

  public interface Callbacks {

  void onCrimeSelected(Crime crime);

  }

  ...

  @Override

  public void onAttach(Activity activity) {

  super.onAttach(activity);

  mCallbacks = (Callbacks) activity;

  }

  @Override

  public void onDetach() {

  super.onDetach();

  mCallbacks = null;

  }

  private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

  ...

   @Override public void onClick(View v) {

   mCallbacks.onCrimeSelected(mCrime);

  }

  }

...

}

  修改之后 : fragment和activity各自关心自己负责的逻辑部分,没有破坏各自的封装性,增强了fragment的可复用性,便于维护

 

  4.fragment和activity的之间通信方式

  a.handler的方案:

    public class MainActivity extends FragmentActivity{

    //声明一个Handler

    public Handler mHandler = new Handler(){

    @Override

    public void handleMessage(Message msg) {

    super.handleMessage(msg); ...相应的处理代码 } } ...相应的处理代码

  }

  public class MainFragment extends Fragment{

  //保存Activity传递的handler

  private Handler mHandler;

  @Override public void onAttach(Activity activity) {

  super.onAttach(activity);

  //这个地方已经产生了耦合,若还有其他的activity,这个地方就得修改

  if(activity instance MainActivity){   

  mHandler = ((MainActivity)activity).mHandler; } } ...相应的处理代码 }

  缺点:Fragment对具体的Activity存在耦合,不利于Fragment复用,没法获取Activity的返回数据

 

  b.接口方案 :  这种方案应该是既能达到复用,又能达到很好的可维护性,并且性能也是杠杠的,但是不利于维护大项目的fragment的使用,包括为接口的命名,新定义的接口相应的Activity还得实现,相应的Fragment还得进行强制转换

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

推荐阅读