Android系统中获取进程(和顶端包名)

概要:

android L前我们可以使用

getRunningTasks(int maxNum)
return RunningTaskInfo
getRunningAppProcesses()
return RunningAppProcessInfo

maxNum int: The maxNumnumber of entries to return in the list. The actual number returned may be smaller, depending on how many tasks the user has started.

通过PackageManager.getApplicationInfo(pakgName, 0)来获取对应包名的应用信息,而在lolipop之后,google在android 5.0 系统中收紧了对API的使用权限,导致我们在使用上边两个方法的时候,有可能只会获取到我们自身应用的包名。针对这一情况,网上也出现了很多的解决方案,今天我们就来了解一二。

获取进程方法:

首先还是来介绍下上边提到的两种方法:
1. getRunningTasks(int maxNum)

ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 1是返回应用列表的size,0是获取当前列表第一个RunningTaskInfo
// RunningTaskInfo.topActivity:The activity component at the top of the history stack of the task.
ComponentName cn = activityManager.getRunningTasks(1).get(0).topActivity;
String currentPkgName = cn.getPackageName(); // 当前显示在顶端的包名

当然熟悉的朋友应该了解这里需要添加一个权限
<uses-permission android:name="android.permission.GET_TASKS" />

This method was deprecated in API level 21. As of LOLLIPOP
this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks , and possibly some other tasks such as home that are known to not be sensitive.

从官方文档可以看到这个方法在5.0之后已经deprecate了。

2. getRunningAppProcesses()

ActivityManager activityManager = (ActivityManager)context.getApplicationContext().getSystemService( Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
  // 如果有需要这里可以遍历当前系统所有的进程
  // String pkgName = processInfo.processName;
  // ApplicationInfo applicationInfo = PackageManager.getApplicationInfo(pkgName, 0);
  
  // 这里开始获取顶端的进程信息
  if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
    Log.i("top_info", "当前顶端进程的信息");
    Log.i("process_pid", "进程id: " + processInfo.pid);
    Log.i("proccess_name", "进程名 : " + processInfo.processName); 
    Log.i("process_pkgs", "该进程下可能存在的包名"); 
    for (String pkgName : processInfo.pkgList) { 
      Log.i("pkg_names", " " + pkgName); 
    } 
    return processInfo.pkgList; 
  } 
}

Returns a list of application processes that are running on the device.

不过在L上获取process还是有一些问题

3. UsageStatsManager
UsageStatsManager是在5.0之后google提供给我们的一个新的API。不过同样的需要用户提供权限。需要我们在获取进程之前,询问是否用户允许了该权限。

  1. 首先添加manifest权限
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" 
tools:ignore="ProtectedPermissions" />

顺带可以看下怎样引导用户开通对应的权限的问题:

  1. 判断是否开启了权限
 @SuppressLint("NewApi")
    public static  boolean hasEnable(Context context){
        if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){   // 如果大于等于5.0 再做判断
            long ts = System.currentTimeMillis();
            UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService(Service.USAGE_STATS_SERVICE);
            List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 0, ts);
            if (queryUsageStats == null || queryUsageStats.isEmpty()) {
                return false;
            }
        }
        return true;
    }
  1. 没有开启权限的跳转
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.setComponent( new ComponentName("com.android.settings", "com.android.settings.Settings$SecuritySettingsActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

顺带提一下,有部分国产厂家的手机系统,在系统安全设置里边是没有有权查看使用情况的应用程序这个选项的。需要注意下。原生系统5.0之后都是有的。

device-2016-11-04-101034.png

  1. 获取顶端的包名(获取一段时间内系统进程)
if (Build.VERSION.SDK_INT >= 21) { 
     try { 
        // 根据最近time_ms毫秒内的应用统计信息进行排序获取当前顶端的包名
        long time = System.currentTimeMillis(); 
        UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
        // 这里返回了在time_ms时间内系统所有的进程列表
        // 如果有获取系统的一段时间之内进程的需要可以打印出每个包名
        List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, time - time_ms, time);
        // 使用queryEvents
        // List<UsageStats> queryUsageStats =  usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 0, time);
        // UsageEvents usageEvents = usageStatsManager.queryEvents(isInit ? 0 : time-time_ms, time);

        //  这里使用的是usageEvent来获取顶端包名
        // String result = "";
        // UsageEvents.Event event = new UsageEvents.Event();
        // UsageEvents usageEvents = usageStatsManager.queryEvents(time - 500, time);
        // while (usageEvents.hasNextEvent()) {
        // usageEvents.getNextEvent(event);
        // if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
        // result = event.getPackageName();
        // Log.e("test", "##当前顶端应用包名:" + result);
        // }
        // }

        if (usageStatsList != null && usageStatsList.size() > 0) { 
            SortedMap<Long, UsageStats> runningTask = new TreeMap<Long, UsageStats>(); 
            for (UsageStats usageStats : usageStatsList) { 
                // Log.e("pkgName", usageStats.getPackageName)
                runningTask.put(usageStats.getLastTimeUsed(), usageStats);
            } 
            if (runningTask.isEmpty()) {
               return null;
            }
            topPackageName = runningTask.get(runningTask.lastKey()).getPackageName(); 
            Log.i("test", "##当前顶端应用包名:" + topPackageName);
           }
     } catch (Throwable e) { 
          e.printStackTrace(); 
    }
}

1.使用这个方法之前需要用户允许应用有权查看组件使用情况,然后才有数据返回
2.新的API能完美进行,就是需要添加新的权限和做一下用户引导
3.如果获取的时间设置得太短,并且这个时间内又没有发生改变的话,那么是获取不到最新的顶端的包名情况的
4.最重要的是,最近又发现在一些机器上(国产为主,魅族领头,LG G3在列),是没有允许应用有权查看使用组件的情况的设置页面的
引用自《获取当前顶端包名》作者还列举了很多的方法,这里就不一一详细介绍了。需要的同学可以自行跳转。

3.通过/proc目录进行判断

/proc目录是存放当前系统运行中的一些信息,包括各个进程,cpu,内存,启动时长等,通过读取该目录下的信息,可以获取系统的进程和顶端包名

说到这里就不得不提下jaredrummler/AndroidProcesses这个GitHub上的类库来获取系统的进程。导入也很方便,不需要添加权限,直接将项目的源码复制到自己项目中也可以使用。也是使用了读取/proc目录来判断系统的进程。

有需要的同学可以看下该类库作者在StackOverflow上的回复。这里只是简要的贴了下代码

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Parcel;import android.os.Parcelable;
import java.util.ArrayList;import java.util.List;
import eu.chainfire.libsuperuser.Shell;
// @author Jared Rummler
public class ProcessManager { 
  private static final String TAG = "ProcessManager";
  private static final String APP_ID_PATTERN; 
  static { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      // Android 4.2 (JB-MR1) changed the UID name of apps for multiple user account support. 
      APP_ID_PATTERN = "u\\d+_a\\d+";
    } else { 
      APP_ID_PATTERN = "app_\\d+"; 
    }
   } 
   public static List<Process> getRunningProcesses() { 
      List<Process> processes = new ArrayList<>(); 
      List<String> stdout = Shell.SH.run("toolbox ps -p -P -x -c");
      for (String line : stdout) { 
      try { 
          processes.add(new Process(line)); 
       } catch (Exception e) { 
          android.util.Log.d(TAG, "Failed parsing line " + line); 
       } 
     } 
      return processes; 
   }
   public static List<Process> getRunningApps() { 
      List<Process> processes = new ArrayList<>();
      List<String> stdout = Shell.SH.run("toolbox ps -p -P -x -c"); 
      int myPid = android.os.Process.myPid(); 
      for (String line : stdout) { 
        try { 
          Process process = new Process(line);
          if (process.user.matches(APP_ID_PATTERN)) {
             if (process.ppid == myPid || process.name.equals("toolbox")) {
                 // skip the processes we created to get the running apps. 
                continue;
             }
            processes.add(process);
          } 
       } catch (Exception e) {
            android.util.Log.d(TAG, "Failed parsing line " + line);
       }
       }
        return processes; 
    } 
    public static class Process implements Parcelable { 
      /** User name */ 
      public final String user; 
      /** User ID */ 
      public final int uid; 
      /** Processes ID */ 
      public final int pid; 
      /** Parent processes ID */ 
      public final int ppid; 
      /** virtual memory size of the process in KiB (1024-byte units). */ 
      public final long vsize; 
      /** resident set size, the non-swapped physical memory that a task has used (in kiloBytes). */ 
      public final long rss; public final int cpu; 
      /** The priority */ 
      public final int priority; 
      /** The priority, <a href="https://en.wikipedia.org/wiki/Nice_(Unix)">niceness</a> level */ 
      public final int niceness; 
      /** Real time priority */ 
      public final int realTimePriority; 
      /** 0 (sched_other), 1 (sched_fifo), and 2 (sched_rr). */ 
      public final int schedulingPolicy; 
      /** The scheduling policy. Either "bg", "fg", "un", "er", or "" */ 
      public final String policy;
       /** address of the kernel function where the process is sleeping */ 
      public final String wchan; public final String pc; 
      /** * Possible states: * 
      <p/> * "D" uninterruptible sleep (usually IO) *
      <p/> * "R" running or runnable (on run queue) * 
      <p/> * "S" interruptible sleep (waiting for an event to complete) * 
      <p/> * "T" stopped, either by a job control signal or because it is being traced * 
      <p/> * "W" paging (not valid since the 2.6.xx kernel) * 
      <p/> * "X" dead (should never be seen) * 
      </p> * "Z" defunct ("zombie")     process, terminated but not reaped by its parent */ 
      public final String state;
      /** The process name */ 
      public final String name; 
      /** user time in milliseconds */ 
      public final long userTime; 
      /** system time in milliseconds */ 
      public final long systemTime; 
      // Much dirty. Much ugly. 
      private Process(String line) throws Exception { 
        String[] fields = line.split("\\s+"); 
        user = fields[0]; 
        uid = android.os.Process.getUidForName(user); 
        pid = Integer.parseInt(fields[1]); 
        ppid = Integer.parseInt(fields[2]);
        vsize = Integer.parseInt(fields[3]) * 1024; 
        rss = Integer.parseInt(fields[4]) * 1024; 
        cpu = Integer.parseInt(fields[5]); 
        priority = Integer.parseInt(fields[6]); 
        niceness = Integer.parseInt(fields[7]); 
        realTimePriority = Integer.parseInt(fields[8]); 
        schedulingPolicy = Integer.parseInt(fields[9]); 
        if (fields.length == 16) { 
          policy = ""; 
          wchan = fields[10]; 
          pc = fields[11]; 
          state = fields[12]; 
          name = fields[13]; 
          userTime = Integer.parseInt(fields[14].split(":")[1].replace(",", "")) * 1000; 
          systemTime = Integer.parseInt(fields[15].split(":")[1].replace(")", "")) * 1000; 
        } else { 
          policy = fields[10];
          wchan = fields[11]; 
          pc = fields[12]; 
          state = fields[13]; 
          name = fields[14]; 
          userTime = Integer.parseInt(fields[15].split(":")[1].replace(",", "")) * 1000;
          systemTime = Integer.parseInt(fields[16].split(":")[1].replace(")", "")) * 1000;
        }
    } 
    private Process(Parcel in) { 
        user = in.readString(); 
        uid = in.readInt(); 
        pid = in.readInt(); 
        ppid = in.readInt(); 
        vsize = in.readLong(); 
        rss = in.readLong(); 
        cpu = in.readInt(); 
        priority = in.readInt(); 
        niceness = in.readInt(); 
        realTimePriority = in.readInt(); 
        schedulingPolicy = in.readInt(); 
        policy = in.readString(); 
        wchan = in.readString(); 
        pc = in.readString(); 
        state = in.readString(); 
        name = in.readString(); 
        userTime = in.readLong(); 
        systemTime = in.readLong(); 
    } 
    public String getPackageName() { 
        if (!user.matches(APP_ID_PATTERN)) { 
          // this process is not an application 
          return null; 
        } else if (name.contains(":")) {
          // background service running in another process than the main app process 
          return name.split(":")[0]; 
        }
        return name; 
    } 
    public PackageInfo getPackageInfo(Context context, int flags) throws PackageManager.NameNotFoundException { 
          String packageName = getPackageName(); 
          if (packageName == null) { 
          throw new PackageManager.NameNotFoundException(name + " is not an application process"); 
          } 
          return context.getPackageManager().getPackageInfo(packageName, flags); 
    }
    public ApplicationInfo getApplicationInfo(Context context, int flags) throws PackageManager.NameNotFoundException { 
        String packageName = getPackageName();
        if (packageName == null) {
        throw new PackageManager.NameNotFoundException(name + " is not an application process"); 
        } 
        return context.getPackageManager().getApplicationInfo(packageName, flags);
    } 
    @Override 
    public int describeContents() { 
        return 0;
    } 
    @Override 
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(user); 
        dest.writeInt(uid);
        dest.writeInt(pid); 
        dest.writeInt(ppid); 
        dest.writeLong(vsize); 
        dest.writeLong(rss); 
        dest.writeInt(cpu); 
        dest.writeInt(priority); 
        dest.writeInt(niceness); 
        dest.writeInt(realTimePriority); 
        dest.writeInt(schedulingPolicy); 
        dest.writeString(policy); 
        dest.writeString(wchan); 
        dest.writeString(pc); 
        dest.writeString(state); 
        dest.writeString(name); 
        dest.writeLong(userTime); 
        dest.writeLong(systemTime); 
    } 
    public static final Creator<Process> CREATOR = new Creator<Process>() { 
    public Process createFromParcel(Parcel source) { 
        return new Process(source);
    } 
    public Process[] newArray(int size) { 
        return new Process[size]; 
    }
 }; 
}
}

Just copy the class into your project if you wish to use it. You will also need to add libsuperuser as a dependency to your build.gradle file:
compile 'eu.chainfire:libsuperuser:1.0.0.+'

作者附带了使用的demo

new AsyncTask<Void, Void, List<ProcessManager.Process>>() {
      long startTime; 
      @Override 
      protected List<ProcessManager.Process> doInBackground(Void... params) {
           startTime = System.currentTimeMillis(); 
           return ProcessManager.getRunningApps(); 
      } 
      @Override 
      protected void onPostExecute(List<ProcessManager.Process> processes) {
           StringBuilder sb = new StringBuilder(); 
           sb.append("Execution time: ").append(System.currentTimeMillis() - startTime).append("ms\n"); 
           sb.append("Running apps:\n"); 
           for (ProcessManager.Process process : processes) { 
               sb.append('\n').append(process.name);
       } 
       new AlertDialog.Builder(MainActivity.this).setMessage(sb.toString()).show(); 
    }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

使用jaredrummler/AndroidProcess获取系统进程

// 当前的系统版本超过5.0
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
    // import com.xxxx.processmanager.ProcessManager;
    // import com.xxxx.processmanager.model.AndroidAppProcess;
    // 这里我是直接把项目的包复制到自己项目中使用
    List<AndroidAppProcess> RunningAppInfos = ProcessManager.getRunningAppProcesses();
    for (AndroidAppProcess AppInfo : RunningAppInfos) { 
       String pkgName = AppInfo.name;
        getAppInfoByPkgName(pkgName, processInfoList);
    }
} else {
    List<ActivityManager.RunningAppProcessInfo> RunningAppInfos = am.getRunningAppProcesses();
    for (ActivityManager.RunningAppProcessInfo appInfo : RunningAppInfos) { 
       String pkgName = appInfo.processName; 
       getAppInfoByPkgName(pkgName, processInfoList);
    }
}
/**     * 通过包名来获取应用的详细信息
     *     * @param pkgName
     * @param processInfoList
     */
    private void getAppInfoByPkgName(String pkgName, List<ProcessInfo> processInfoList) {
        PackageManager pm = getPackageManager();
        ProcessInfo processInfo = new ProcessInfo();
        // 通过使用包名来获取对应应用的详细信息
        try {
            ApplicationInfo applicationInfo = pm.getApplicationInfo(pkgName, 0);
            // 获取应用名字
            String appName = applicationInfo.loadLabel(pm).toString();
            processInfo.setName(appName);
            // 获取应用的图标
            Drawable icon = applicationInfo.loadIcon(pm);
            processInfo.setIcon(icon);
            if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                // 用户进程
                processInfo.setUser(true);
            } else {
                // 系统进程
                processInfo.setUser(false);
            }
            processInfo.setPkgName(pkgName);
            processInfoList.add(processInfo);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            // 系统内核进程 没有名称
            processInfo.setPkgName(pkgName);
            processInfo.setName("系统内核进程");
            processInfo.setUser(false);
            processInfoList.add(processInfo);
        }
        // 排序
        Collections.sort(processInfoList);
//        for (ProcessInfo info : processInfoList) {
//            Log.e("appName", info.getName());
//            Log.e("isSys", info.isUser() + "");
//        }
    }

接下来是ProcessInfo 类(实现了排序功能)

import android.graphics.drawable.Drawable;
/** * Created by Admin on 2016/11/2.
 * description list process info of search result
 */
public class ProcessInfo implements Comparable<ProcessInfo> {
    private String name;
    private String pkgName;
    private int pid;
    private Drawable icon;
    public boolean isUser() {
        return isUser;
    }
    public void setUser(boolean user) {
        isUser = user;
    }
    private boolean isUser;// sys = false;
    public Drawable getIcon() {
        return icon;
    }
    public void setIcon(Drawable icon) {
        this.icon = icon;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPkgName() {
        return pkgName;
    }
    public void setPkgName(String pkgName) {
        this.pkgName = pkgName;
    }
    public int getPid() {
        return pid;
    }
    public void setPid(int pid) {
        this.pid = pid;
    }
    @Override
    public int compareTo(ProcessInfo processInfo) {
        if (isUser == true && processInfo.isUser == false) {
            return -1;
        } else if (isUser == true && processInfo.isUser == true) {
            return 0;
        } else {
            return 1;
        }
    }
}

到此处基本可以完成获取系统进程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,657评论 0 33
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,292评论 18 399
  • 一个人最好的生活状态,有喜欢的一件事做,有爱的人,也有爱你的人,能养自己的家,父母健康,朋友不多但死党有几个,你好...
    笃学青衿阅读 101评论 0 0
  • 茶从离开茶树那一刻起,就期待着与水相逢;水唤醒茶,茶成就水;水包容茶,茶丰富水;茶因水而重生,水因茶而清香。 ...
    馬荣軍阅读 406评论 0 1