zoukankan      html  css  js  c++  java
  • DiyCode开源项目 BaseActivity 分析

      1.首先将这个项目的BaseActivity源码拷贝过来。

    /*
     * Copyright 2017 GcsSloop
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     * Last modified 2017-03-11 22:24:54
     *
     * GitHub:  https://github.com/GcsSloop
     * Website: http://www.gcssloop.com
     * Weibo:   http://weibo.com/GcsSloop
     */
    
    package com.gcssloop.diycode.base.app;
    
    import android.annotation.TargetApi;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.annotation.LayoutRes;
    import android.support.v7.app.ActionBar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.text.TextUtils;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.Toast;
    
    import com.gcssloop.diycode.R;
    import com.gcssloop.diycode.hackpatch.IMMLeaks;
    import com.gcssloop.diycode_sdk.api.Diycode;
    
    import java.io.Serializable;
    
    public abstract class BaseActivity extends AppCompatActivity {
    
        protected Diycode mDiycode;//项目本身的api提供的一个调用类
        protected ViewHolder mViewHolder;
        private Toast mToast;
    
        @TargetApi(Build.VERSION_CODES.KITKAT)
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mDiycode = Diycode.getSingleInstance();
            mViewHolder = new ViewHolder(getLayoutInflater(), null, getLayoutId());
            setContentView(mViewHolder.getRootView());
            IMMLeaks.fixFocusedViewLeak(this.getApplication()); // 修复 InputMethodManager 引发的内存泄漏
            initActionBar(mViewHolder);
            initDatas();
            initViews(mViewHolder, mViewHolder.getRootView());
        }
    
    
        @LayoutRes
        protected abstract int getLayoutId();
    
        /**
         * 初始化数据,调用位置在 initViews 之前
         */
        protected void initDatas() {
        }
    
        /**
         * 初始化 View, 调用位置在 initDatas 之后
         */
        protected abstract void initViews(ViewHolder holder, View root);
    
    
        // 初始化 ActiobBar
        private void initActionBar(ViewHolder holder) {
            Toolbar toolbar = holder.get(R.id.toolbar);
            if (toolbar != null) {
                setSupportActionBar(toolbar);
            }
            ActionBar actionBar = getSupportActionBar();
            if (actionBar != null) {
                actionBar.setDisplayHomeAsUpEnabled(true);
            }
        }
    
        // 默认点击左上角是结束当前 Activity
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case android.R.id.home:
                    finish();
                    break;
            }
            return super.onOptionsItemSelected(item);
        }
    
    
        public ViewHolder getViewHolder() {
            return mViewHolder;
        }
    
        /**
         * 发出一个短Toast
         *
         * @param text 内容
         */
        public void toastShort(String text) {
            toast(text, Toast.LENGTH_SHORT);
        }
    
        /**
         * 发出一个长toast提醒
         *
         * @param text 内容
         */
        public void toastLong(String text) {
            toast(text, Toast.LENGTH_LONG);
        }
    
    
        private void toast(final String text, final int duration) {
            if (!TextUtils.isEmpty(text)) {
                runOnUiThread(new Runnable() {
    
                    @Override
                    public void run() {
                        if (mToast == null) {
                            mToast = Toast.makeText(getApplicationContext(), text, duration);
                        } else {
                            mToast.setText(text);
                            mToast.setDuration(duration);
                        }
                        mToast.show();
                    }
                });
            }
        }
    
    
        protected void openActivity(Class<?> cls) {
            openActivity(this, cls);
        }
    
        public static void openActivity(Context context, Class<?> cls) {
            Intent intent = new Intent(context, cls);
            context.startActivity(intent);
        }
    
        /**
         * 打开 Activity 的同时传递一个数据
         */
        protected <V extends Serializable> void openActivity(Class<?> cls, String key, V value) {
            openActivity(this, cls, key, value);
        }
    
    
        /**
         * 打开 Activity 的同时传递一个数据
         */
        public <V extends Serializable> void openActivity(Context context, Class<?> cls, String key, V value) {
            Intent intent = new Intent(context, cls);
            intent.putExtra(key, value);
            context.startActivity(intent);
        }
    
    }
    View Code

     

      2.Diycode是项目本身的api提供的一个调用类。放在module中,名字为:diycode-sdk,应该就是为了方便开发客户端提供的一个API了,里面又是一大坨,单单这个类就有一千行,很多逻辑函数,不过现在先不研究。

     

      3.ViewHolder是什么东西呢?

       ==>其实就是性能优化,这里是自己创建的一个类。放在和BaseActivity同一级下,所以说这个东西应该和BaseActivity一样,经常使用,以前也只是看到别人用,就自己用一下,不懂得其中真正的含义。

       网上搜了一篇文章:listview加载性能优化ViewHolder

     

         这个项目的ViewHolder源码是这样的:

    /*
     * Copyright 2017 GcsSloop
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     * Last modified 2017-03-11 22:24:54
     *
     * GitHub:  https://github.com/GcsSloop
     * Website: http://www.gcssloop.com
     * Weibo:   http://weibo.com/GcsSloop
     */
    
    package com.gcssloop.diycode.base.app;
    
    import android.content.Context;
    import android.support.annotation.NonNull;
    import android.util.SparseArray;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.bumptech.glide.Glide;
    import com.bumptech.glide.load.engine.DiskCacheStrategy;
    import com.gcssloop.diycode_sdk.log.Logger;
    
    public class ViewHolder {
    
        private SparseArray<View> mViews;
        private View mRootView;
    
        public ViewHolder(LayoutInflater inflater, ViewGroup parent, int layoutId) {
            this.mViews = new SparseArray<View>();
            mRootView = inflater.inflate(layoutId, parent, false);
        }
    
        /**
         * 通过View的id来获取子View
         *
         * @param resId view的id
         * @param <T>   泛型
         * @return 子View
         */
        public <T extends View> T get(int resId) {
            View view = mViews.get(resId);
            //如果该View没有缓存过,则查找View并缓存
            if (view == null) {
                view = mRootView.findViewById(resId);
                mViews.put(resId, view);
            }
            if (view == null){
                Logger.e("View == null");
            }
            return (T) view;
        }
    
        /**
         * 获取布局View
         *
         * @return 布局View
         */
        public View getRootView() {
            return mRootView;
        }
    
        /**
         * 设置文本
         *
         * @param res_id view 的 id
         * @param text   文本内容
         * @return 是否成功
         */
        public boolean setText(CharSequence text, @NonNull int res_id) {
            try {
                TextView textView = get(res_id);
                textView.setText(text);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    
        public boolean setText(@NonNull int res_id, CharSequence text) {
            return setText(text, res_id);
        }
    
        public void loadImage(Context context, String url, int res_id) {
            ImageView imageView = get(res_id);
            String url2 = url;
            if (url.contains("diycode"))
                url2 = url.replace("large_avatar", "avatar");
            Glide.with(context).load(url2).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);
        }
    
        /**
         * 设置监听器
         *
         * @param l   监听器
         * @param ids view 的 id
         */
        public void setOnClickListener(View.OnClickListener l, int... ids) {
            if (ids == null) {
                return;
            }
            for (int id : ids) {
                get(id).setOnClickListener(l);
            }
        }
    }
    View Code

      下面分析一下:

        (1)有一个私有成员变量。private SparseArray<View> mViews;

          了解一下SpareseArray

          类似一个容器,可以装一些视图,并且这个类方便使用,性能也很棒。

        (2)然后是ViewHolder构造函数,参数分别为LayoutInflater inflater,ViewGroup parent,Int layoutId.现在仔细了解一下这几个参数吧。

          ①LayoutInflater是什么东西呢?==>主要就是将xml转换成一个View对象,动态创建布局。

           inflate方法有三个参数,分别是resource==>布局的资源id,root==>填充的根视图,attachToRoot==>是否将载入的视图绑定到根视图。

          ②View与ViewGroup有什么区别?==>View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的。

          ③LayoutId其实就是自己项目中R.id.***,就是inflate中的布局的资源的id。

        (3)通过view的id来获取子View    

     /**
         * 通过View的id来获取子View
         *
         * @param resId view的id
         * @param <T>   泛型
         * @return 子View
         */
        public <T extends View> T get(int resId) {
            View view = mViews.get(resId);
            //如果该View没有缓存过,则查找View并缓存
            if (view == null) {
                view = mRootView.findViewById(resId);
                mViews.put(resId, view);
            }
            if (view == null){
                Logger.e("View == null");
            }
            return (T) view;
        }
    View Code

          ①<T extends View>是一个类型,如何理解这个泛型呢?==>限制了返回的T类,必须是View的子类。

          ②这里用到了一个Logger,日志类,直接拷贝一下,这个东西也是为了方便调试的。基本上每个项目都会用到的。  

    /*
     * Copyright 2017 GcsSloop
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     * Last modified 2017-03-10 00:33:05
     *
     * GitHub:  https://github.com/GcsSloop
     * Website: http://www.gcssloop.com
     * Weibo:   http://weibo.com/GcsSloop
     */
    
    package com.gcssloop.diycode_sdk.log;
    
    import android.support.annotation.NonNull;
    import android.util.Log;
    
    public class Logger {
    
        private static String DEFAULT_TAG = "GCS-LOG";
    
        private static Config mConfig;
    
        private Logger() {
    
        }
    
        public static Config init() {
            mConfig = new Config(DEFAULT_TAG);
            return mConfig;
        }
    
        public static Config init(@NonNull String tag) {
            mConfig = new Config(tag);
            return mConfig;
        }
    
        public static void v(String message) {
            log(Config.LEVEL_VERBOSE, mConfig.getTag(), message);
        }
    
        public static void d(String message) {
            log(Config.LEVEL_DEBUG, mConfig.getTag(), message);
        }
    
        public static void i(String message) {
            log(Config.LEVEL_INFO, mConfig.getTag(), message);
        }
    
        public static void w(String message) {
            log(Config.LEVEL_WARN, mConfig.getTag(), message);
        }
    
        public static void e(String message) {
            log(Config.LEVEL_ERROR, mConfig.getTag(), message);
        }
    
        public static void v(String tag, String message) {
            log(Config.LEVEL_VERBOSE, tag, message);
        }
    
        public static void d(String tag, String message) {
            log(Config.LEVEL_DEBUG, tag, message);
        }
    
        public static void i(String tag, String message) {
            log(Config.LEVEL_INFO, tag, message);
        }
    
        public static void w(String tag, String message) {
            log(Config.LEVEL_WARN, tag, message);
        }
    
        public static void e(String tag, String message) {
            log(Config.LEVEL_ERROR, tag, message);
        }
    
        private static void log(int level, String tag, String message) {
            if (mConfig.getLevel() == Config.LEVEL_NONE) {
                return;
            }
    
            if (level < mConfig.getLevel()) {
                return;
            }
    
            switch (level) {
                case Config.LEVEL_VERBOSE:
                    Log.v(tag, message);
                    break;
                case Config.LEVEL_DEBUG:
                    Log.d(tag, message);
                    break;
                case Config.LEVEL_INFO:
                    Log.i(tag, message);
                    break;
                case Config.LEVEL_WARN:
                    Log.w(tag, message);
                    break;
                case Config.LEVEL_ERROR:
                    Log.e(tag, message);
                    break;
            }
    
        }
    }
    View Code

                Logger类中有定义一个Config类,这是在diycode_sdk里面的,先看看定义吧。

    /*
     * Copyright 2017 GcsSloop
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     * Last modified 2017-03-10 00:33:05
     *
     * GitHub:  https://github.com/GcsSloop
     * Website: http://www.gcssloop.com
     * Weibo:   http://weibo.com/GcsSloop
     */
    
    package com.gcssloop.diycode_sdk.log;
    
    import android.support.annotation.NonNull;
    
    public class Config {
    
        public static final int LEVEL_NONE = 0;
        public static final int LEVEL_FULL = 1;
    
        public static final int LEVEL_VERBOSE = 2;
        public static final int LEVEL_DEBUG = 3;
        public static final int LEVEL_INFO = 4;
        public static final int LEVEL_WARN = 5;
        public static final int LEVEL_ERROR = 6;
        public static final int LEVEL_ASSERT = 7;
    
        private String tag;
        private int level;
    
        public Config(String tag) {
            this.tag = tag;
            level = LEVEL_FULL;
        }
    
        public Config setLevel(@NonNull int level){
            this.level = level;
            return this;
        }
    
        public int getLevel() {
            return level;
        }
    
        public String getTag() {
            return tag;
        }
    }
    View Code

                感觉像一个Bean类,很简单的一个类,定义了7个静态整型常量,1,2,3,4,5,6,7,应该是日志的等级,分为7个模式吧。

        (4)有注意到有些变量前面有@NonNull注解,不知道什么意思。==>指明一个参数,字段或者方法的返回值不可以为NULL。

           (5)然后在ViewHolder中提供了setText给TextView。方式很简单,就是先通过id得到这个TextView,然后setText即可。

        (6)然后在ViewHolder中提供了一个loadImage方法。比较复杂,贴一下源代码。   

    public void loadImage(Context context, String url, int res_id) {
            ImageView imageView = get(res_id);
            String url2 = url;
            if (url.contains("diycode"))
                url2 = url.replace("large_avatar", "avatar");
            Glide.with(context).load(url2).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);
        }
    View Code

     

          ①首先也是通过id得到这个imageView。

          ②这里有一个Glide类。不知道这个图片加载库怎么用?

     

          

        (7)最后设置了一个监听器。两个参数:View.OnClickListener l,int... ids  ==>省略号是什么意思?==>类似一个数组吧(而且事先不知道长度呢!)

      4.@TargetApi(Build.VERSION_CODES.KITKAT)在onCreate的前面声明,这东西起什么作用呢? ==>使用高编译版本的代码,为了通用性兼容运行此代码的低版本平台,要求程序员做出区分对待的加载。         

     

      5.在BaseActivity中的onCreate函数中,通过Diycode的SDK中获取单例类。这里了解一下什么是单例模式。==>保证一个类仅有一个实例,并提供一个访问它的全局访问点。

     

      6.这里谈一下onCreate函数中的setContentView(view)

        ==>默认Activity中放入我们的xml或者Java控件是通过setContentView方法来操作的,当调用了setContentView所有的控件就得到了显示。

      7.如何解决InputMethodManager造成的内存泄漏问题?

       看一下这篇文章。

        

      8.onCreate函数中有一个初始化ActionBar,就是初始化标题栏的意思。

        ToolBar代替Actionbar在AppCompatActivity的使用

        这里解释一下:

        (1)actionBar.setDisplayHomeAsUpEnabled(true)==>给左上角图标的左边加上一个返回的图标。对应                   ActionBar.DISPLAY_HOME_AS_UP。

         (2)actionBar.setDisplayShowHomeEnabled(true) //使左上角图标是否显示,如果设成false,则没有程序图标,仅仅就个标题,否则,显示应用程序图标,对应id为android.R.id.home,对应ActionBar.DISPLAY_SHOW_HOME

        (3)actionBar.setDisplayShowCustomEnabled(true) // 使自定义的普通View能在title栏显示,即actionBar.setCustomView能起作用,对应ActionBar.DISPLAY_SHOW_CUSTOM

        (4)actionBar.setDisplayShowTitleEnabled(true) //对应ActionBar.DISPLAY_SHOW_TITLE。

        (5)其中setHomeButtonEnabled和setDisplayShowHomeEnabled共同起作用,如果setHomeButtonEnabled设成false,即使setDisplayShowHomeEnabled设成true,图标也不能点击。

     

      9.在BaseActivity中有一个toast函数,采用了一个runOnUiThread(new Runnable(){里面复写一个run方法即可})

        代码参考一下了:  

    private void toast(final String text, final int duration) {
            if (!TextUtils.isEmpty(text)) {
                runOnUiThread(new Runnable() {
    
                    @Override
                    public void run() {
                        if (mToast == null) {
                            mToast = Toast.makeText(getApplicationContext(), text, duration);
                        } else {
                            mToast.setText(text);
                            mToast.setDuration(duration);
                        }
                        mToast.show();
                    }
                });
            }
        }
    View Code

        不了解runOnUiThread?==>runOnUiThread更新主线程。

       

      10.打开一个活动,在BaseActivity中,定义一个方法,点击某个东西,跳转到另外一个活动。

     protected void openActivity(Class<?> cls) {
            openActivity(this, cls);
        }
    
        public static void openActivity(Context context, Class<?> cls) {
            Intent intent = new Intent(context, cls);
            context.startActivity(intent);
        }
    View Code

      11.打开一个活动的同时传递一个数据

     /**
         * 打开 Activity 的同时传递一个数据
         */
        protected <V extends Serializable> void openActivity(Class<?> cls, String key, V value) {
            openActivity(this, cls, key, value);
        }
    
    
        /**
         * 打开 Activity 的同时传递一个数据
         */
        public <V extends Serializable> void openActivity(Context context, Class<?> cls, String key, V value) {
            Intent intent = new Intent(context, cls);
            intent.putExtra(key, value);
            context.startActivity(intent);
        }
    View Code

       总结一下:

      1.这个BaseActivity应该指的是大部分通用的一个活动,左上角是一个返回的图标,右上角自定义的一个导航栏。

      2.然后这个BaseActivity提供了一个toast支持,支持长toast或者短toast,而且是采用runOnUiThread更新主线程的方式。所以不会阻塞的。

      3.这个BaseActivity中有自定义ViewHolder,加强了性能。里面可以有TextView,ImageView,还可以设置监听器给每一个view。

      4.这个BaseActivity存放了一个关键的类,Diycode可以方便地调用API,获取后端数据的关键。

      5.有一个抽象函数initViews(ViewHolder,View root),那么在继承这个BaseActivity中就可以执行了。

      6.最后定义了2个重载函数,作用就是可以跳转到另外一个活动,传数据或者不传数据。

     

    既然选择了,便不顾风雨兼程。Just follow yourself.
  • 相关阅读:
    单例模式(singleton)
    Unsupported major.minor version 51.0
    “万能数据库查询分析器”4.03发布,谨以此致我们终将逝去的青春
    和菜鸟一起学linux内核之初始化init篇
    PL/SQL备份oracle数据库
    java.lang.ClassCastException: org.apache.struts.taglib.bean.CookieTei
    DB Query Analyzer 4.03 is upgraded in ZOL
    Android应用程序资源的查找过程分析
    java.lang.IllegalArgumentException: Can't convert argument: null
    Dalvik虚拟机简要介绍和学习计划
  • 原文地址:https://www.cnblogs.com/Jason-Jan/p/7831925.html
Copyright © 2011-2022 走看看