1. 程式人生 > >android 廣播+服務+Application結合案例:獲取所有安裝應用的資訊並展示到listview上,結合SearchView進行檢索

android 廣播+服務+Application結合案例:獲取所有安裝應用的資訊並展示到listview上,結合SearchView進行檢索

效果圖:

思路:

  • 1、建立自定義Application,在其onCreate()回撥方法中啟動一個Service,在Service中開啟一個Thread執行緒,在該執行緒中,使用PackageManger類(主要職責是管理應用程式包)的getInstalledApplications()方法獲取已安裝的應用程式資訊,並將獲取的資料集合(型別為List<ApplicationInfo>)通過Intent傳送給自定義廣播。
  • 2、在廣播的回撥方法中,接收來自1中傳來的資料,並將其作為資料來源設定給ListView的介面卡。
  • 3、在SearchView的回撥方法中, 迴圈使用loadLabel(PackageManager pm)  獲得當前應用程式的label與使用者輸入的字串進行包含比較,再建立List<ApplicationInfo>集合,如果包含就新增到此集合。最後將該集合重新作為資料來源繫結ListView的介面卡。

佈局很簡單:

1。main_acitivity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <android.support.v7.widget.SearchView
        android:layout_margin="10dp"
        android:id="@+id/main_sv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:searchHintIcon="@drawable/ic_action_action_search"
        android:background="@drawable/sv_bg"
        app:iconifiedByDefault="false"
        app:queryHint="請輸入...">
    </android.support.v7.widget.SearchView>

    <ListView
        android:id="@+id/main_lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </ListView>

</LinearLayout>

2。list_item.xml 介面卡每一個item的佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_margin="10dp"
        tools:background="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:textSize="20dp"
        tools:text="測試測試" />

</LinearLayout>

程式碼:

bean類 封裝你想要app的資訊類 

AppInfos.java  因為涉及intent傳輸需實現Parcelable方法

import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * <p>檔案描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>建立時間:2018/11/24 18:14<p>
 * <p>更改時間:2018/11/24 18:14<p>
 * <p>版本號:1<p>
 */

/**
 * app應用資訊封裝類
 */
public class AppInfos implements Parcelable {

    private CharSequence label;
    private Drawable img;

    public AppInfos(CharSequence label, Drawable img) {
        this.label = label;
        this.img = img;
    }


    public CharSequence getLabel() {
        return label;
    }

    public void setLabel(CharSequence label) {
        this.label = label;
    }

    public Drawable getImg() {
        return img;
    }

    public void setImg(Drawable img) {
        this.img = img;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }

    protected AppInfos(Parcel in) {
    }

    public static final Creator<AppInfos> CREATOR = new Creator<AppInfos>() {
        @Override
        public AppInfos createFromParcel(Parcel in) {
            return new AppInfos(in);
        }

        @Override
        public AppInfos[] newArray(int size) {
            return new AppInfos[size];
        }
    };
}

MyApliacation.java類 全域性的進行開啟服務


import android.app.Application;
import android.content.Intent;

/**
 * <p>檔案描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>建立時間:2018/11/24 14:51<p>
 * <p>更改時間:2018/11/24 14:51<p>
 * <p>版本號:1<p>
 */
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        //1.開啟服務
        startService(new Intent(this, MyService.class));
    }
}

MyService.java 進行服務的一系列操作 看註釋

主要是註冊,登出,傳送廣播


import android.app.Service;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.util.Log;

import com.demo.showapp.config.Config;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>檔案描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>建立時間:2018/11/24 14:52<p>
 * <p>更改時間:2018/11/24 14:52<p>
 * <p>版本號:1<p>
 */
public class MyService extends Service {

    private static final String TAG = "MyService";

    //1.建立服務
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        //開啟執行緒
        new Thread() {
            @Override
            public void run() {
                super.run();
                Log.d(TAG, "run: 開啟獲取app資訊的程序");
                //1.獲取app資訊
                PackageManager pm = getPackageManager();
                List<ApplicationInfo> mLists = pm.getInstalledApplications(0);
                Log.d(TAG, "run: " + mLists.get(1).loadLabel(pm));
                Intent i = new Intent();
                i.setAction(Config.BROADCAST_ACTION);
                i.putParcelableArrayListExtra(Config.APP_INFO, (ArrayList<? extends Parcelable>) mLists);
                //2.傳送廣播
                sendBroadcast(i);
            }
        }.start();
        return super.onStartCommand(intent, flags, startId);


    }

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

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind: ");
        return super.onUnbind(intent);
    }

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

Config.java 全域性配置類,配置一些共有的資訊

public class Config {
    public static final String SEARCH_TEXT = "search_text";
    public static final String APP_INFO = "app_Info";
    public static final String BROADCAST_ACTION = "com.demo.action";
}

ListAdapter.java 基礎BaseAdapter

在裡面實現了SearchView的檢索

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.demo.showapp.R;
import com.demo.showapp.bean.AppInfos;
import com.squareup.picasso.Picasso;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>檔案描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>建立時間:2018/11/24 16:18<p>
 * <p>更改時間:2018/11/24 16:18<p>
 * <p>版本號:1<p>
 */

/**
 * 裡面實現的思路是:
 * 1.新建一個List new_number這個物件用來儲存過濾後符合條件的值
 * 2.判斷constraint是否為空,是否有值.滿足條件的話遍歷List,並且判斷List的值是否有包含過濾的條件.
 * 3.如果是的話把值存到new_number.如果一個符合的都沒有把temp_number的值(也就是最開始傳進來的List)賦值給new_number
 * 4.新建一個FilterResults物件分別把new_number的size和value賦值給FilterResults.value和FilterResults.count,然後返回FilterResults物件
 * 5.接著在publishResults()方法中取出過濾後滿足條件的值也就是results.values
 * 6.判斷results.count的過濾後結果的個數是否大於0
 * 7.大於0的話把值賦值給number然後重新整理介面,不滿足的話把temp_number(也就是最開始傳進來的值)賦值給number,然後重新整理介面
 */
public class ListAdapter extends BaseAdapter implements Filterable {

    private Context mContext;
    private List<AppInfos> mAppInfos, temp_Infos;
    private LayoutInflater mLayoutInflater;
    private PackageManager pm;
    private TestFilter myFilter;

    public ListAdapter(Context context, List<AppInfos> appInfos) {
        mAppInfos = appInfos;
        temp_Infos = appInfos;
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
        pm = context.getPackageManager();
    }

    @Override
    public int getCount() {
        return mAppInfos.size();
    }

    @Override
    public Object getItem(int position) {
        return mAppInfos.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder vh;
        if (convertView == null) {
            vh = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.list_item, null, false);
            vh.icon = convertView.findViewById(R.id.iv_icon);
            vh.title = convertView.findViewById(R.id.tv_title);
            convertView.setTag(vh);
        } else {
            vh = (ViewHolder) convertView.getTag();
        }
        vh.icon.setBackground(mAppInfos.get(position).getImg());
        vh.title.setText(mAppInfos.get(position).getLabel());
        return convertView;
    }

    /**
     * 實現了Filterable介面,這個方法需要覆寫getFilter()方法.
     *
     * @return
     */
    @Override
    public Filter getFilter() {
        if (myFilter == null) {
            myFilter = new TestFilter();
        }
        return myFilter;
    }

    private class ViewHolder {
        private ImageView icon;
        private TextView title;
    }


    class TestFilter extends Filter {
        /**
         * 該方法主要完成對資料進行過濾的工作
         *
         * @param constraint
         * @return
         */
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {

            List<AppInfos> new_number = new ArrayList();
            if (constraint != null && constraint.toString().trim().length() > 0) {
                for (int i = 0; i < temp_Infos.size(); i++) {
                    String content = (String) temp_Infos.get(i).getLabel();
                    Drawable img = temp_Infos.get(i).getImg();
                    if (content.contains(constraint)) {
                        new_number.add(new AppInfos(content, img));
                    }
                }

            } else {
                new_number = temp_Infos;
                Log.e("MainActivity","adapter");
            }

            FilterResults filterResults = new FilterResults();
            filterResults.count = new_number.size();
            filterResults.values = new_number;
            return filterResults;
        }

        /**
         * 接收過濾後資料
         * 在該方法裡面進行介面的重新整理工作
         */
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {

            mAppInfos = (List<AppInfos>) results.values;

            if (results.count > 0) {
                Toast.makeText(mContext, "查詢成功", Toast.LENGTH_SHORT).show();
                notifyDataSetChanged();
            } else {
                Toast.makeText(mContext, "暫無應用", Toast.LENGTH_SHORT).show();
                notifyDataSetChanged();
            }

        }
    }


}

MainActivity.java

1.註冊廣播,接受傳來的資訊

2.橫豎屏的狀態儲存

3.按返回鍵後重新回來恢復狀態 


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import android.icu.util.LocaleData;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Filter;
import android.widget.ListView;
import android.widget.TextView;

import com.demo.showapp.adapter.ListAdapter;
import com.demo.showapp.bean.AppInfos;
import com.demo.showapp.config.Config;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ListAdapter mAadpter;
    private ListView lv;
    private SearchView sv;
    private List<AppInfos> mInfos = new ArrayList<>();
    private String search_text = "";
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "onReceive: 拿到broadcast傳送來的資訊");
            if (intent.getAction().equals(Config.BROADCAST_ACTION)) {
                List<ApplicationInfo> infos = intent.getParcelableArrayListExtra(Config.APP_INFO);
                //將應用資訊裝到新的集合中
                for (ApplicationInfo info : infos) {
                    CharSequence label = info.loadLabel(getPackageManager());
                    Drawable icon = info.loadIcon(getPackageManager());
                    mInfos.add(new AppInfos(label, icon));
                }
                initView();
            }

        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate: ");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 初始化檢視
     */
    private void initView() {
        lv = findViewById(R.id.main_lv);
        sv = findViewById(R.id.main_sv);
        mAadpter = new ListAdapter(this, mInfos);
        lv.setAdapter(mAadpter);
        searchViewListener();
    }

    /**
     * searchView的監聽事件
     */
    public void searchViewListener() {

        sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            //提交的text
            @Override
            public boolean onQueryTextSubmit(String s) {
                Log.d(TAG, "onQueryTextSubmit: " + s);
                //Adapter的內容進行過濾.
                mAadpter.getFilter().filter(s);
                return false;
            }

            //改變的text
            @Override
            public boolean onQueryTextChange(String s) {
                Log.d(TAG, "onQueryTextChange: " + s);
                //儲存全域性變數
                search_text = s;
                //Adapter的內容進行過濾.
                mAadpter.getFilter().filter(s);
                return false;
            }
        });
    }


    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
        //註冊廣播
        registerReceiver();
    }

    /**
     * 動態註冊廣播
     */
    private void registerReceiver() {
        //1.意圖過濾
        Log.d(TAG, "registerReceiver: ");
        IntentFilter mFilter = new IntentFilter();
        mFilter.addAction(Config.BROADCAST_ACTION);
        //2.註冊廣播
        registerReceiver(mBroadcastReceiver, mFilter);
    }

    /**
     * 登出廣播
     */
    @Override
    protected void onPause() {
        Log.d(TAG, "onPause: ");
        super.onPause();
        unregisterReceiver(mBroadcastReceiver);
    }

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

    /**
     * 返回時儲存狀態
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        try {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                Intent home = new Intent(Intent.ACTION_MAIN);
                home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                home.addCategory(Intent.CATEGORY_HOME);
                startActivity(home);
                return true;
            }
        } catch (Exception e) {
            //ignore

        }
        return super.onKeyDown(keyCode, event);
    }

    /**
     * 橫豎屏切換儲存值
     * 原理:
     * 1.橫屏切換豎屏實際上是先把當前的橫屏的Activity殺掉,然後重新建立一個豎屏的Activity
     * 2.我們可以使用onSaveInstanceState()方法儲存資料
     * 3.它是在橫屏Activity將殺死前呼叫,可以將須要儲存的資料放入Bundle封裝在系統中
     * 4.切換豎屏後這個Activity又重新被建立 這樣可以在onCreate(Bundle)
     * 5.或者onRestoreInstanceState(Bundle)方法中來回復之前儲存在Bundle中的資料
     * 6.這樣就可以實現橫豎屏介面切換資料的儲存與讀取
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        Log.d(TAG, "onSaveInstanceState: ");
        super.onSaveInstanceState(outState);
        outState.putString(Config.SEARCH_TEXT, search_text);
        outState.putParcelableArrayList(Config.APP_INFO, (ArrayList<? extends Parcelable>) mInfos);
    }

    /**
     * 恢復值
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.d(TAG, "onRestoreInstanceState: ");
        super.onRestoreInstanceState(savedInstanceState);
        if (savedInstanceState != null || !savedInstanceState.getParcelableArrayList(Config.APP_INFO).isEmpty()) {
            mInfos = savedInstanceState.getParcelableArrayList(Config.APP_INFO);
            search_text = savedInstanceState.getString(Config.SEARCH_TEXT);
            Log.d(TAG, "onRestoreInstanceState: 恢復資料");
            if (mInfos != null || !mInfos.isEmpty()) {
                initView();
                //設定預設的查詢值為上次輸入的值
                sv.setQuery(search_text, true);
            }
        }


    }
}

完成。需要demo留下郵箱看到後回覆