• 每日一点点Android的积攒


    一、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还得进行强制转换

      

  • 相关阅读:
    D Prefix XORs
    Navicat_查看Navicat已保存数据库密码
    Django_报错:Forbidden (CSRF cookie not set.): /runoob/ [21/Mar/2022 19:48:12] "POST /runoob/ HTTP/1.1" 403 2801
    Django_配置数据库后重启报错:django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required; you have 0.10.1.
    C# this.Invoke()与Delegate.Invoke()区别使用多线程循环执行(定时执行)数据处理功能
    Wince系统Pad串口连接外接设备
    c# Invoke和BeginInvoke的区别推荐文章
    C# this.BeginInvoke()与Delegate.BeginInvoke()的区别
    winfrom程序实时更新UI,程序长时间运行,不过我使用的线程和委托运行两天就自动退出了,不知道怎么回事??
    十全十美的文本日志类
  • 原文地址:https://www.cnblogs.com/liunx1109/p/11050185.html
Copyright © 2020-2023  润新知