美文网首页
Android基本点总结

Android基本点总结

作者: EdwardWinner | 来源:发表于2018-07-31 13:52 被阅读7次

1.Bitmap 具体的方法

Bitmap.recycle() 内存
如果bitmap没有回收,会造成什么问题?
首先bitmap对象会向内存申请一片空间,当bitmap对象被gc回收时,但是内存却没有回收,
所以会造成内存泄漏,也就是OOM,解决办法就是及时的调用bitmap.recycle()

方法:

BitmapFactory.decodeFile(filePath, null);
BitmapFactory.decodeStream(inputStream);
BitmapFactory.decodeResource(getResources(),R.drawable.haha);

BitmapFactory.Options options = new BitmapFactory.Options();
首先options.inJustDecodeBounds = true;
再调用BitmapFactory.decodeFile(path,options);得到图片的基本信息
再利用图像的宽/高,得到inSampleSize值,
再将options.inJustDecodeBounds = false;
再将options.inDither = false;
再将options.inPreferredConfig = Bitmap.Config.ARGB_8888;
再调用BitmapFactory.decodeFile(path,options);

2软引用/弱引用

首先定义一个HashMap,保存软引用对象。

private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
再来定义一个方法,保存Bitmap的软引用到HashMap。
public void addBitmapToCache(String path){
        // 强引用的Bitmap对象
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        // 软引用的Bitmap对象
        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
        // 添加该对象到Map中使其缓存
        imageCache.put(path, softBitmap);
}
获取的时候,可以通过SoftReference的get()法得到Bitmap对象。
public Bitmap getBitmapByPath(String path) {
        // 从缓存中取软引用的Bitmap对象
        SoftReference<Bitmap> softBitmap = imageCache.get(path);
        // 判断是否存在软引用
        if (softBitmap == null) {
            return null;
        }
        // 取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空
        Bitmap bitmap = softBitmap.get();
        return bitmap;
}

个人认为,如果只是想避免OutOfMemory异常的 发生,则可以使用软引用。
如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

3.ImageLoader cache

4.线程池 数量/缓存/时间段

ThreadPoolExecutor

new ThreadPoolExecutor(initPoolSize, maxPoolSize, KEEP_ALIVE_TIME,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
new PriorityThreadFactory("thread-pool",android.os.Process.THREAD_PRIORITY_BACKGROUND));

5.异步处理 AsyncTask doInbackground

AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdateonPostExecute。这四个方法会在AsyncTask的不同时期进行自动调用,我们只需要实现这几个方法的内部逻辑即可。这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result

  • Params表示用于AsyncTask执行任务的参数的类型
  • Progress表示在后台线程处理的过程中,可以阶段性地发布结果的数据类型
  • Result表示任务全部完成后所返回的数据类型

该方法有MainThread注解,表示该方法是运行在主线程中的。在AsyncTask执行了execute()方法后就会在UI线程上执行onPreExecute()方法,该方法在task真正执行前运行,我们通常可以在该方法中显示一个进度条,从而告知用户后台任务即将开始。

该方法有WorkerThread注解,表示该方法是运行在单独的工作线程中的,而不是运行在主线程中。doInBackground会在onPreExecute()方法执行完成后立即执行,该方法用于在工作线程中执行耗时任务,我们可以在该方法中编写我们需要在后台线程中运行的逻辑代码,由于是运行在工作线程中,所以该方法不会阻塞UI线程。该方法接收Params泛型参数,参数params是Params类型的不定长数组,该方法的返回值是Result泛型,由于doInBackgroud是抽象方法,我们在使用AsyncTask时必须重写该方法。在doInBackground中执行的任务可能要分解为好多步骤,每完成一步我们就可以通过调用AsyncTask的publishProgress(Progress…)将阶段性的处理结果发布出去,阶段性处理结果是Progress泛型类型。当调用了publishProgress方法后,处理结果会被传递到UI线程中,并在UI线程中回调onProgressUpdate方法,下面会详细介绍。根据我们的具体需要,我们可以在doInBackground中不调用publishProgress方法,当然也可以在该方法中多次调用publishProgress方法。doInBackgroud方法的返回值表示后台线程完成任务之后的结果。

该方法也具有MainThread注解,表示该方法是在主线程上被调用的,且传入的参数是Progress泛型定义的不定长数组。如果在doInBackground中多次调用了publishProgress方法,那么主线程就会多次回调onProgressUpdate方法。

该方法也具有MainThread注解,表示该方法是在主线程中被调用的。当doInBackgroud方法执行完毕后,就表示任务完成了,doInBackgroud方法的返回值就会作为参数在主线程中传入到onPostExecute方法中,这样就可以在主线程中根据任务的执行结果更新UI。

public class MainActivity extends Activity implements Button.OnClickListener {

    TextView textView = null;
    Button btnDownload = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView)findViewById(R.id.textView);
        btnDownload = (Button)findViewById(R.id.btnDownload);
        Log.i("iSpring", "MainActivity -> onCreate, Thread name: " + Thread.currentThread().getName());
    }

    @Override
    public void onClick(View v) {
        //要下载的文件地址
        String[] urls = {
                "http://blog.csdn.net/iispring/article/details/47115879",
                "http://blog.csdn.net/iispring/article/details/47180325",
                "http://blog.csdn.net/iispring/article/details/47300819",
                "http://blog.csdn.net/iispring/article/details/47320407",
                "http://blog.csdn.net/iispring/article/details/47622705"
        };

        DownloadTask downloadTask = new DownloadTask();
        downloadTask.execute(urls);
    }

    //public abstract class AsyncTask<Params, Progress, Result>
    //在此例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型
    private class DownloadTask extends AsyncTask<String, Object, Long> {
        @Override
        protected void onPreExecute() {
            Log.i("iSpring", "DownloadTask -> onPreExecute, Thread name: " + Thread.currentThread().getName());
            super.onPreExecute();
            btnDownload.setEnabled(false);
            textView.setText("开始下载...");
        }

        @Override
        protected Long doInBackground(String... params) {
            Log.i("iSpring", "DownloadTask -> doInBackground, Thread name: " + Thread.currentThread().getName());
            //totalByte表示所有下载的文件的总字节数
            long totalByte = 0;
            //params是一个String数组
            for(String url: params){
                //遍历Url数组,依次下载对应的文件
                Object[] result = downloadSingleFile(url);
                int byteCount = (int)result[0];
                totalByte += byteCount;
                //在下载完一个文件之后,我们就把阶段性的处理结果发布出去
                publishProgress(result);
                //如果AsyncTask被调用了cancel()方法,那么任务取消,跳出for循环
                if(isCancelled()){
                    break;
                }
            }
            //将总共下载的字节数作为结果返回
            return totalByte;
        }

        //下载文件后返回一个Object数组:下载文件的字节数以及下载的博客的名字
        private Object[] downloadSingleFile(String str){
            Object[] result = new Object[2];
            int byteCount = 0;
            String blogName = "";
            HttpURLConnection conn = null;
            try{
                URL url = new URL(str);
                conn = (HttpURLConnection)url.openConnection();
                InputStream is = conn.getInputStream();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buf = new byte[1024];
                int length = -1;
                while ((length = is.read(buf)) != -1) {
                    baos.write(buf, 0, length);
                    byteCount += length;
                }
                String respone = new String(baos.toByteArray(), "utf-8");
                int startIndex = respone.indexOf("<title>");
                if(startIndex > 0){
                    startIndex += 7;
                    int endIndex = respone.indexOf("</title>");
                    if(endIndex > startIndex){
                        //解析出博客中的标题
                        blogName = respone.substring(startIndex, endIndex);
                    }
                }
            }catch(MalformedURLException e){
                e.printStackTrace();
            }catch(IOException e){
                e.printStackTrace();
            }finally {
                if(conn != null){
                    conn.disconnect();
                }
            }
            result[0] = byteCount;
            result[1] = blogName;
            return result;
        }

        @Override
        protected void onProgressUpdate(Object... values) {
            Log.i("iSpring", "DownloadTask -> onProgressUpdate, Thread name: " + Thread.currentThread().getName());
            super.onProgressUpdate(values);
            int byteCount = (int)values[0];
            String blogName = (String)values[1];
            String text = textView.getText().toString();
            text += "\n博客《" + blogName + "》下载完成,共" + byteCount + "字节";
            textView.setText(text);
        }

        @Override
        protected void onPostExecute(Long aLong) {
            Log.i("iSpring", "DownloadTask -> onPostExecute, Thread name: " + Thread.currentThread().getName());
            super.onPostExecute(aLong);
            String text = textView.getText().toString();
            text += "\n全部下载完成,总共下载了" + aLong + "个字节";
            textView.setText(text);
            btnDownload.setEnabled(true);
        }

        @Override
        protected void onCancelled() {
            Log.i("iSpring", "DownloadTask -> onCancelled, Thread name: " + Thread.currentThread().getName());
            super.onCancelled();
            textView.setText("取消下载");
            btnDownload.setEnabled(true);
        }
    }
}
  • 我们在MainActivity中定义了内部类DownloadTask,DownloadTask继承自AsyncTask,在该例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型。

  • 我们定义了一个Url字符串数组,将该数组传递给AsyncTask的execute方法,用于异步执行task。

  • 在执行了downloadTask.execute(urls)之后,AsyncTask会自动回调onPreExecute方法,在该方法中我们将textView设置为“开始下载…”几个字,告知用户即将执行下载操作。通过控制台输出我们也可以看出该方法是在主线程中执行的。

  • 在执行了onPreExecute方法之后,AsyncTask会回调doInBackground方法,该方法中的输入参数是String类型的不定长数组,此处的String就对应着Params泛型类型,我们在该方法中遍历Url数组,依次下载对应的文件,当我们下载完一个文件,就相当于我们阶段性地完成了一部分任务,我们就通过调用publishProgress方法将阶段性处理结果发布出去。在此例中我们将阶段性的处理结果定义为Object类型,即Progress泛型类型。通过控制台输出我们可以看出doInBackground方法是运行在新的工作线程”AsyncTask #1”中的,AsyncTask的工作线程都是以”AsyncTask #”然后加上数字作为名字。当所有文件下载完成后,我们就可以通过totalSize返回所有下载的字节数,返回值类型为Long,对应着AsyncTask中的Result泛型类型。

  • 在doInBackground方法中,每当下载完一个文件,我们就会调用publishProgress方法发布阶段性结果,之后AsyncTask会回调onProgressUpdate方法,在此例中,onProgressUpdate的参数为Object类型,对应着AsyncTask中的Progress泛型类型。通过控制台输出我们可以发现,该方法是在主线程中调用的,在该方法中我们会通过textView更新UI,告知用户哪个文件下载完成了,这样用户体验相对友好。

  • 在整个doInBackground方法执行完毕后,AsyncTask就会回调onPostExecute方法,在该方法中我们再次通过textView更新UI告知用户全部下载任务完成了。

  • 在通过execute方法执行了异步任务之后,可以通过AsyncTask的cancel方法取消任务,取消任务后AsyncTask会回调onCancelled方法,这样不会再调用onPostExecute方法。

6.网络请求 post/get请求

网络超时,404url,500服务器 ,responseCode == 200
HttpUrlConnection setRequstMethod()..,HttpPost/HttpGet

数据传输 param ,xml流,json JsonArray,JsonObject,XmlPullParser

7.局部显示图片

8.方法重写/重载

方法重写:

子类继承父类,重写了父类的非私有的方法,这也是多态性的一种体现。意义在于能够定义子类特有的特征。

方法重载:

就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。

调用被重载过的方法可以大大减少代码的输入量,同一个方法名只要往里面传递不同的参数就可以拥有不同的功能或返回值。这样一个类的结构就会变得清晰和简洁。

9.动画

Android动画效果translate、scale、alpha、rotate详解动画类型
Android的animation由四种类型组成

XML中
  • alpha 渐变透明度动画效果
  • scale 渐变尺寸伸缩动画效果
  • translate 画面转换位置移动动画效果
  • rotate 画面转移旋转动画效果
JavaCode中
  • AlphaAnimation 渐变透明度动画效果
  • ScaleAnimation 渐变尺寸伸缩动画效果
  • TranslateAnimation 画面转换位置移动动画效果
  • RotateAnimation 画面转移旋转动画效果
Animation alphaAnimation = new AlphaAnimation(0.1f, 1.0f);
其中AlphaAnimation类第一个参数fromAlpha表示动画起始时的透明度, 第二个参数toAlpha表示动画结束时的透明度。
alphaAnimation.setDuration(2000);
this.startAnimation(alphaAnimation);

Animation rotateAnimation = new RotateAnimation(0f, 360f);
其中RotateAnimation类第一个参数fromDegrees表示动画起始时的角度, 第二个参数toDegrees表示动画结束时的角度。
rotateAnimation.setDuration(2000);
this.startAnimation(alphaAnimation);

Animation scaleAnimation = new ScaleAnimation(0.1f, 1.0f,0.1f,1.0f);
第一个参数fromX ,第二个参数toX:分别是动画起始、结束时X坐标上的伸缩尺寸。
第三个参数fromY ,第四个参数toY:分别是动画起始、结束时Y坐标上的伸缩尺寸。
另外还可以设置伸缩模式pivotXType、pivotYType, 伸缩动画相对于x,y 坐标的开始位置pivotXValue、pivotYValue等
scaleAnimation.setDuration(500);
this.startAnimation(scaleAnimation);

Animation translateAnimation = new TranslateAnimation(0.1f, 100.0f,0.1f,100.0f);
第一个参数fromXDelta ,第二个参数toXDelta:分别是动画起始、结束时X坐标。
第三个参数fromYDelta ,第四个参数toYDelta:分别是动画起始、结束时Y坐标。
translateAnimation.setDuration(1000);
this.startAnimation(translateAnimation);
Animation主要有两种动画模式:

一种是tweened animation(渐变动画)

XML中 JavaCode
alpha AlphaAnimation
scale ScaleAnimation
translate TranslateAnimation
rotate RotateAnimation

一种是frame by frame(画面转换动画)

10.Activity的4中启动方式

1.standard :

每次激活Activity时(startActivity),都创建Activity实例,并放入任务栈;

2.singleTop :

如果某个Activity自己激活自己,即任务栈栈顶就是该Activity,则不需要创建,其余情况都要创建Activity实例;

3.singleTask:

如果要激活的那个Activity在任务栈中存在该实例,则不需要创建,只需要把此Activity放入栈顶,并把该Activity以上的Activity实例都pop;

4.singleInstance:

如果应用1的任务栈中创建了MainActivity实例,如果应用2也要激活MainActivity,则不需要创建,两应用共享该Activity实例;这个Activity,存在一个新的任务栈中,只创建一次,如果这个时候启动其他的Activity,则这个Activity所在的任务栈位置互换。退出的时候依次退出。

11.Intent显示/隐示

  • Intent 显示意图:直接根据包名+类名
  • Intent 隐示意图:根据Action来查找。

优点和缺点:

  • 显示意图高耦合,高响应
  • 隐示意图低耦合,低响应

12.重点放在图片

13.面向对象的3大特征:

封装,继承,多态

多态:父类型的引用可以指向子类型的对象。

多态的好处:

  • 1.可替换性
  • 2.可扩充性
  • 3.接口性
  • 4.灵活性
  • 5.简化性

(1).向上类型转换:将子类类型转换成父类类型(不强转)
(2).向下类型转换:将父类类型转换成子类类型(强转)

继承:

封装:
对于封装而言,一个对象它所封装的是自己的属性和方法。

封装的好处:

1.能够减少耦合
2.类内部的结构可以自由修改
3.对成员进行更精确的控制
4.隐藏信息,对外提供set/get方法。

14.广播

BroadcastReceiver广播接收者
同一个APP(同一组件/不同组件/多个进程)消息通信 不同APP组件之间消息通信
Android系统在特定情况下与APP之间的消息通信
静态注册
exported true ->> 可以接收其他App的发出的广播
可以定义Action

<receiver android:name="com.tct.gallery3d.picturegrouping.NewPictureBroadcastReceiver"
            android:enabled="true"
            android:exported="true" >

            <intent-filter>
                <action android:name="android.hardware.action.NEW_PICTURE" />
                <action android:name="com.android.camera.NEW_PICTURE" />
                <data android:mimeType="image/*" />
            </intent-filter>

            <intent-filter>
                <action android:name="android.hardware.action.NEW_VIDEO" />
                <action android:name="com.android.camera.NEW_VIDEO" />
                <data android:mimeType="video/*" />
            </intent-filter>

            <intent-filter>
                <action android:name="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
                <data android:scheme="file" />
                <data android:mimeType="image/*"/>
            </intent-filter>

            <intent-filter>
                <action android:name="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
                <data android:scheme="file" />
                <data android:mimeType="video/*"/>
            </intent-filter>
</receiver>

**

registerReceiver(BroadcastReceiver receiver,IntentFilter filter);

onStart();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(mBroadcastReceiver,intentFilter);

onStop();
unRegisterReceiver(mBroadcastReceiver);

private BroadcastReceiver firstFrameReceiver = new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent) {
            String path = intent.getStringExtra("com.muvee.faceshow.FRAME_PATH");
            mFirstFrameBitmap = BitmapFactory.decodeFile(path);
            Log.d(TAG, "mFirstFrameBitmap = " + mFirstFrameBitmap);
            mHandler.obtainMessage(MSG_UPDATE_BITMAP,null).sendToTarget();
        }
};

动态注册

广播发送:

Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.putExtra("name","eric");
sendBroadcast(intent);

广播的分类:

  • 普通广播:
  • 系统广播:
  • 有序广播:priority 属性从大到小
  • 粘性广播:

15.Service

1.Context.startService();
它的生命周期:

  • startService-->onCreate();--->onStart();
  • stopService();---> onStop();

2.Context.bindService();
生命周期:
绑定时:

  • bindService()->onCreate()-->onBind();
  • 解除绑定:unbindService()->onUnBind()-->onDestory();
Intent intent = new Intent();
bindService(intent,conn,Context.BIND_AUTO_CREATE);

unBindService(conn);
private ServiceConnection conn = new ServiceConnection(ComponentName name,iBinder service){
    MyBinder binder = (MyBinder)service;
    BinderService service = binder.getService1();
    service.method();
}

private class BinderService extends Service{

  private MyBinder myBinder = new MyBinder();

  public void method(){
    Log.e("TAG","method");
  }

  private class MyBinder extends Binder{

  public BinderService getService1(){
    return BinderService.this;
  }
}

16.进程之间通信:

  • 1).Activity之间的通信:

在android系统中有很多应用程序提供了可以跨进程访问的Activity,例如,下面的代码可以直接调用拨打电话的Activity。

Intent callIntent = new  Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" );  
startActivity(callIntent);
  • 2).Content Provider
    Android应用程序可以使用文件或SqlLite数据库来存储数据。Content Provider提供了一种在多个应用程序之间数据共享的方式(跨进程共享数据)。应用程序可以利用Content Provider完成下面的工作

    1. 查询数据
    1. 修改数据
    1. 添加数据
    1. 删除数据

虽然Content Provider也可以在同一个应用程序中被访问,但这么做并没有什么意义。Content Provider存在的目的向其他应用程序共享数据和允许其他应用程序对数据进行增、删、改操作。

Android系统本身提供了很多Content Provider,例如,音频、视频、联系人信息等等。我们可以通过这些Content Provider获得相关信息的列表。这些列表数据将以Cursor对象返回。因此,从Content Provider返回的数据是二维表的形式。

... ...
public class DictionaryContentProvider extends ContentProvider{
    private static UriMatcher uriMatcher;
    private static final String AUTHORITY = "net.blogjava.mobile.dictionarycontentprovider";
    private static final int SINGLE_WORD = 1;
    private static final int PREFIX_WORDS = 2;
    public static final String DATABASE_PATH = android.os.Environment
    .getExternalStorageDirectory().getAbsolutePath()
    + "/dictionary";
    public static final String DATABASE_FILENAME = "dictionary.db";
    private SQLiteDatabase database;
    static{
        //  添加访问ContentProvider的Uri
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "single", SINGLE_WORD);
        uriMatcher.addURI(AUTHORITY, "prefix/*", PREFIX_WORDS);
    }
    //  该方法在Activity的onCreate方法之前调用
    @Override
    public boolean onCreate(){
        database = openDatabase();
        return true;
    }
    //  在本例中只实现了query方法,其他的方法(insert、update和delete)与query方法的实现
    //  类似
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder){
        Cursor cursor = null;
        switch (uriMatcher.match(uri)){
            case SINGLE_WORD:
                //  查找指定的单词
                cursor = database.query("t_words", projection, selection,
                        selectionArgs, null, null, sortOrder);
                break;
            case PREFIX_WORDS:
                String word = uri.getPathSegments().get(1);
                //  查找以指定字符串开头的单词集合
                cursor = database
                        .rawQuery(
                                "select english as _id, chinese from t_words where english like ?",
                                new String[]
                                { word + "%" });
                break;

            default:
                throw new IllegalArgumentException("<" + uri + ">格式不正确.");
        }
        return cursor;
    }
    ... ...
}
  • 3).广播 Broadcast

广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。
在应用程序中发送广播比较简单。只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。

  • 4).Service
    服务(Service)是android系统中非常重要的组件。Service可以脱离应用程序运行。也就是说,应用程序只起到一个启动Service的作用。一但Service被启动,就算应用程序关闭,Service仍然会在后台运行。

android系统中的Service主要有两个作用:后台运行和跨进程通讯。后台运行就不用说了,当Service启动后,就可以在Service对象中运行相应的业务代码,而这一切用户并不会察觉。而跨进程通讯是这一节的主题。如果想让应用程序可以跨进程通讯,就要使用我们这节讲的AIDL服务,AIDL的全称是Android Interface Definition Language,也就是说,AIDL实际上是一种接口定义语言。通过这种语言定义接口后,Eclipse插件(ODT)会自动生成相应的Java代码接口代码。

17.Gson的使用:

JSONObject的使用方法

//org.json.JSONArray;
//org.json.JSONObject;
private void parseJSONWithJSONObject(String jsonData){
    try {
        //将json字符串jsonData装入JSON数组,即JSONArray
        //jsonData可以是从文件中读取,也可以从服务器端获得
        JSONArray jsonArray = new JSONArray(jsonData);
        for (int i = 0; i< jsonArray.length(); i++) {
            //循环遍历,依次取出JSONObject对象
            //用getInt和getString方法取出对应键值
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            int stu_no = jsonObject.getInt("stu_no");
            String stu_name = jsonObject.getString("stu_name");
            String stu_sex = jsonObject.getString("stu_sex");
            Log.d("MainActivity","stu_no: " + stu_no);
            Log.d("MainActivity","stu_name: " + stu_name);
            Log.d("MainActivity","stu_sex: " + stu_sex);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
字符串jsonData如下,图为运行结果
[{ "stu_no":12345,"stu_name":"John","stu_sex":"male"
},{ "stu_no":12346,"stu_name":"Tom","stu_sex":"male"
},{"stu_no":12347,"stu_name":"Lily","stu_sex":"female"}]
  • toJson(params1),将传入对象转换为字符串
  • fromJson(params1,params2),传入两个参数,将字符串params1转换为params2指定的数据类型。
public class Student {
    private int stu_no;
    private String stu_name;
    private String stu_sex;
 
    Student(int stu_no,String stu_name,String stu_sex){
        this.stu_no = stu_no;
        this.stu_name = stu_name;
        this.stu_sex = stu_sex;
    }
}
 
// 序列化,将Student对象stu转换为字符串str
Student stu = new Student(123,"Tom","male");
Gson gson = new Gson();
String str = gson.toJson(stu); 
 
//反序列化,将字符串转换为Student对象
jsonData = "{ \"stu_no\":12345,\"stu_name\":\"John\",\"stu_sex\":\"male\" }";
Gson gson = new Gson();
Student student = gson.fromJson(jsonData,Student.class);

JSON数组的解析(原生类)

Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
 
//序列化(serialization)
//将整数数组转换为JSON数组
gson.toJson(ints);     // ==> [1,2,3,4,5]
//将字符串数组转换为JSON数组
gson.toJson(strings);  // ==> ["abc", "def", "ghi"]
 
// 反序列化(Deserialization)
// 将JSON数组转换为原生类数组
// ints2、string2与ints、strings相等
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
String[] strings2 = gson.fromJson("[\"abc\", \"def\", \"ghi\"]",String[].class);

JSON数组的解析(自定义类)

//对于类似于2.2中的jsonData,包含3个Student对象
//与原生类不同,需要借助TypeToken获得期望解析成的数据类型
//下列代码运行后,students包含三个Student对象
Gson gson = new Gson();
List<Student> students;
students = gson.fromJson(jsonData, new TypeToken<List<Student>>(){}.getType()); // ==>[stu0,stu1,stu2]

18、单线程模型中Message、Handler、Message Queue、Looper之间的关系。

简单的说,Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message,再有Handler进行Message的分发和处理.

Message Queue(消息队列)

用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列

Handler

可以发布或者处理一个消息或者操作一个Runnable,通过Handler发布消息,消息将只会发送到与它关联的消息队列,然也只能处理该消息队列中的消息

Looper:

是Handler和消息队列之间通讯桥梁,程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的

Handler:Handler接受到消息后调用handleMessage进行处理

Message:

消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理


在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:

1. Message

Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。

2. Handler

Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。

3. MessageQueue

Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。

4. Looper

Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,API Doc提供了正确的使用方法:这个Message机制的大概流程:

    1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。
    1. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
  1. Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
  1. Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
  1. 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。

由此可见,我们实现的handleMessage方法是优先级最低的!

    1. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
  1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;
  1. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。

19、Service和Thread的区别?

Servie:

是系统的组件,它由系统进程托管(servicemanager);它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是binder,它是在linux层交换信息的一种ipc。Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。

Thread:

Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。而thread是由本应用程序托管。

既然这样,那么我们为什么要用 Service 呢?

其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

举个例子:

如果你的 Thread需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

因此你可以把Service想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

20.IntentService

public class VerifyIntentService extends IntentService {

    private static final String TAG = "VerifyIntentService";
    private static final String SERVICE_NAME = "VerifyService";

    public static final String MSG_TASK_FIRST = "msg_task_first";
    public static final String MSG_TASK_SECOND = "msg_task_second";
    public static final String TASK_NAME = "Task_Name";

    public VerifyIntentService() {
        super(SERVICE_NAME);
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.d(TAG, "onStart");
        super.onStart(intent, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return super.onBind(intent);
    }

    @Override
    public void setIntentRedelivery(boolean enabled) {
        super.setIntentRedelivery(enabled);
        Log.d(TAG, "setIntentRedelivery");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String taskName = intent.getExtras().getString(TASK_NAME);
        switch (taskName) {
            case MSG_TASK_FIRST:
                Log.d(TAG, "start task 1");
                break;
            case MSG_TASK_SECOND:
                Log.d(TAG, "start task 2");
                break;
            default:
                break;
        }
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

}


public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private Button mStartTaskButton1;
    private Button mStartTaskButton2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }

    private void initViews() {
        mStartTaskButton1 = (Button) this.findViewById(R.id.msg_task1);
        mStartTaskButton2 = (Button) this.findViewById(R.id.msg_task2);

        mStartTaskButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startIntentService1();
            }
        });

        mStartTaskButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startIntentService2();
            }
        });
    }

    private void startIntentService1() {
        Intent intentFirst = new Intent(this, VerifyIntentService.class);
        Bundle bundle = new Bundle();
        bundle.putString(VerifyIntentService.TASK_NAME, VerifyIntentService.MSG_TASK_FIRST);
        intentFirst.putExtras(bundle);
        startService(intentFirst);
    }

    private void startIntentService2() {
        Intent intentSecond = new Intent(this, VerifyIntentService.class);
        Bundle bundle = new Bundle();
        bundle.putString(VerifyIntentService.TASK_NAME, VerifyIntentService.MSG_TASK_SECOND);
        intentSecond.putExtras(bundle);
        startService(intentSecond);
    }
}

IntentService:

异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务。

IntentService有以下特点:

  • 它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。
  • 创建了一个工作队列,来逐个发送intent给onHandleIntent()。
  • 不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。
  • 默认实现的onBind()返回null
  • 默认实现的onStartCommand()的目的是将intent插入到工作队列中

部分转自Android爱好者的总结,如果问题,请联系本人。
有新的内容,将会持续更新,谢谢!

相关文章

网友评论

      本文标题:Android基本点总结

      本文链接:https://www.haomeiwen.com/subject/xfxhvftx.html