日期: 2021 年 4 月 27 日

Android 内存优化简介

1、内存占用高导致的问题

  • 1、内存泄漏导致OOM崩溃
  • 2、界面卡顿,影响用户体验
  • 3、高内存耗电,被系统及安全软件警告,容易被卸载或后台关闭

之所以要内存优化为了更好的适应JVM的GC机制,减少对程序的影响(减少卡顿)。

2、JVM 内存模型

Minor GC 使用标记-清除-复制算法,将Eden区和From Survivor区中存活下来的对象复制到 To Survivor区中,然后清空Eden区和From Survivor区,速度快。

Major GC 采用标记-清除-压缩算法,即,先标记无用的对象,然后将存活下来的对象移动,整理碎片,以腾出更大的连续空间。

如下图所示:

%title插图%num
JVM.png

3、GC原理及简介

采用是否根节点可访问判断是否需要回收对象,如下图所示

%title插图%num
GC.png
3.1标记清除复制算法
%title插图%num
mark-copy.png
3.2标记清除复制算法
%title插图%num
mark-press.png

4、GC Reason和Name

<pre class=”hljs undefined” data-original-code=”” reason”=”” data-snippet-id=”ext.fad1dd2cb080f9ce19557e96ccdfb127″ data-snippet-saved=”false” data-codota-status=”done”>

  1. Reason
  2. Concurrent—-后台回收内存,不暂停用户线程
  3. Alloc—-当app要申请内存,而堆又快满了的时候,会阻塞用户线程
  4. Explicit—-调用Systemt.gc()等方法的时候触发,一般不建议使用
  5. NativeAlloc—-当native内存有压力的时候触发
  6. Name
  7. Concurrent mark sweep—-全部对象的检测回收
  8. Concurrent partial mark sweep—-部分的检测回收
  9. Concurrent sticky mark sweep—-仅检测上次回收后创
  10. 建的对象,速度快,卡顿少,比较频繁

GC log 示例

04-06 09:41:48.541 5021-5045/com.husor.beibei I/art: Background partial concurrent mark sweep GC freed 22647(1359KB) AllocSpace objects, 5(969KB) LOS objects, 6% free, 53MB/57MB, paused 5.128ms total 68.744ms

5、如何进行内存优化

  • 1、消除内存泄漏
  • 2、使用高性能编程
  • 3、降低程序运行的内存占用

6、常见内存泄漏和高内存占用原因

  • 1、慎重使用static变量
  • 2、长周期内部类、匿名内部类长时间持有外部类引用导致相关资源无法释放(Handler或者内部线程等)
  • 3、BitMap导致内存溢出
  • 4、数据库、文件流等没有关闭
  • 5、监听器、广播注册后没有及时注销
  • 6、Adapter没有使用convertView
  • 7、字符串拼接尽量使用StringBuilder或者StringBuffer
  • 8、避免内存抖动,例如不要在onDraw中创建对象。
  • 9、界面不可见时,停止动画和相关线程
6.1、示例一:
%title插图%num
sample1.png

原因:
sBackground, 是一个静态的变量,但是我们发现,我们并没有显式的保存Contex的引用,但是,当Drawable与View连接之后,Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。这个引用链如下:Drawable->TextView->Context

解决办法:

  1. *,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
  2. 第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长
  3. 第三、使用WeakReference代替强引用。比如WeakReference<Context> mContextRef; (已经不推荐)
6.1、示例二:
%title插图%num
sample2.png

原因:

Activity退出后,其实例并未被回收。因为OneThread作为非静态内部类还持有BasicActivity的实例。

解决方案:

  1. 1、OneThread改为静态内部类
  2. 2、如果OneThread需要Context实例,使用弱引用保存它。
  3. 3、如果有必要,在BasicActivity的OnDestroy里面关闭线程

7、内存分析工具

7、1 Android Monitor
%title插图%num
util1.png

** 优点:**
使用简单,直观显示当前app的内存变化
缺点:
无法具体定位内存问题,只能给出内存笼统的变化。
常用于分析内存变化趋势。

7、2 Allocation Tracker
%title插图%num
util2.png
  1. 1、选择进程,在合适的实际开始和结束追踪。
  2. 2、Studio会自动打开文件,里面展示这一段时间内内存的分配情况,可以分析自己的程序哪里内存占用比较高,是否有大量的相同类型的 Object 被分配和释放。如果有,则其可能引起性能问题。
7、3 MAT

步骤:

  • 1生成mat文件

在android studio 生成dump文件,并转为标准dump。
如下图所示

%title插图%num
mat1.png
%title插图%num
mat2.png
  • 2使用mat分析
  • 以内部线程导致内存泄漏为例分析:
    1、多次打开该demo,生成dump文件
    2、打开MAT工具,选择“File”>>“open heap dump”,打开dump文件
    3、打开后在,“Action”里的“Histogram”,然后搜索SecondActivity
%title插图%num
mat3.png
  • 3猜测分析原因

    通过上面看到,存在11个SecondActivity的实例,有点诡异。
    选择SecondActivity这一项,然后右键“Merge Shortest Paths to GC Roots ”>>”exclude all phantom/weak/soft etc references..”

%title插图%num
mat4.png
%title插图%num
mat5.png
7、4LeakCanary 检测内存泄漏
leakcanary.png

直接可以通过通知栏看到内存泄露的地方

Android App优化之ANR详解

App优化系列已近中期, 前面分享了一些工具, 理论, 也结合了案例谈了下启动优化, 布局分析等.

原计划将本文作为这个系列的一个承上启下点, 对前面几篇作一个小总结, 聊聊App流畅度和快速响应的话题.

粗一缕, 发现内容还是很多, 暂且拆成几篇来慢慢写吧, 勿怪~

今天先来聊聊ANR.

1, 你碰到ANR了吗

在App使用过程中, 你可能遇到过这样的情况:

%title插图%num
ANR

恭喜你, 这就是传说中的ANR.

1.1 何为ANR

ANR全名Application Not Responding, 也就是”应用无响应”. 当操作在一段时间内系统无法处理时, 系统层面会弹出上图那样的ANR对话框.

1.2 为什么会产生ANR

在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的. 通常在如下两种情况下会弹出ANR对话框:

  • 5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
  • BroadcastReceiver在10s内无法结束.

造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等等.

1.3 如何避免ANR

知道了ANR产生的原因, 那么想要避免ANR, 也就很简单了, 就一条规则:

不要在主线程(UI线程)里面做繁重的操作.

这里面实际上涉及到两个问题:

  1. 哪些地方是运行在主线程的?
  2. 不在主线程做, 在哪儿做?

稍后解答.

2, ANR分析

2.1 获取ANR产生的trace文件

ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过adb命令将其导出到本地:

$adb pull data/anr/traces.txt .

2.2 分析traces.txt

2.2.1 普通阻塞导致的ANR

获取到的tracs.txt文件一般如下:

如下以GithubApp代码为例, 强行sleep thread产生的一个ANR.

  1. ----- pid 2976 at 2016-09-08 23:02:47 -----
  2. Cmd line: com.anly.githubapp // *新的ANR发生的进程(包名)
  3. ...
  4. DALVIK THREADS (41):
  5. "main" prio=5 tid=1 Sleeping
  6. | group="main" sCount=1 dsCount=0 obj=0x73467fa8 self=0x7fbf66c95000
  7. | sysTid=2976 nice=0 cgrp=default sched=0/0 handle=0x7fbf6a8953e0
  8. | state=S schedstat=( 0 0 0 ) utm=60 stm=37 core=1 HZ=100
  9. | stack=0x7ffff4ffd000-0x7ffff4fff000 stackSize=8MB
  10. | held mutexes=
  11. at java.lang.Thread.sleep!(Native method)
  12. - sleeping on <0x35fc9e33> (a java.lang.Object)
  13. at java.lang.Thread.sleep(Thread.java:1031)
  14. - locked <0x35fc9e33> (a java.lang.Object)
  15. at java.lang.Thread.sleep(Thread.java:985) // 主线程中sleep过长时间, 阻塞导致无响应.
  16. at com.tencent.bugly.crashreport.crash.c.l(BUGLY:258)
  17. - locked <@addr=0x12dadc70> (a com.tencent.bugly.crashreport.crash.c)
  18. at com.tencent.bugly.crashreport.CrashReport.testANRCrash(BUGLY:166) // 产生ANR的那个函数调用
  19. - locked <@addr=0x12d1e840> (a java.lang.Class<com.tencent.bugly.crashreport.CrashReport>)
  20. at com.anly.githubapp.common.wrapper.CrashHelper.testAnr(CrashHelper.java:23)
  21. at com.anly.githubapp.ui.module.main.MineFragment.onClick(MineFragment.java:80) // ANR的起点
  22. at com.anly.githubapp.ui.module.main.MineFragment_ViewBinding$2.doClick(MineFragment_ViewBinding.java:47)
  23. at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)
  24. at android.view.View.performClick(View.java:4780)
  25. at android.view.View$PerformClick.run(View.java:19866)
  26. at android.os.Handler.handleCallback(Handler.java:739)
  27. at android.os.Handler.dispatchMessage(Handler.java:95)
  28. at android.os.Looper.loop(Looper.java:135)
  29. at android.app.ActivityThread.main(ActivityThread.java:5254)
  30. at java.lang.reflect.Method.invoke!(Native method)
  31. at java.lang.reflect.Method.invoke(Method.java:372)
  32. at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
  33. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

拿到trace信息, 一切好说.
如上trace信息中的添加的中文注释已基本说明了trace文件该怎么分析:

  1. 文件*上的即为*新产生的ANR的trace信息.
  2. 前面两行表明ANR发生的进程pid, 时间, 以及进程名字(包名).
  3. 寻找我们的代码点, 然后往前推, 看方法调用栈, 追溯到问题产生的根源.

以上的ANR trace是属于相对简单, 还有可能你并没有在主线程中做过于耗时的操作, 然而还是ANR了. 这就有可能是如下两种情况了:

2.2.2 CPU满负荷

这个时候你看到的trace信息可能会包含这样的信息:

  1. Process:com.anly.githubapp
  2. ...
  3. CPU usage from 3330ms to 814ms ago:
  4. 6% 178/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20 major
  5. 4.6% 2976/com.anly.githubapp: 0.7% user + 3.7% kernel /faults: 52 minor 19 major
  6. 0.9% 252/com.android.systemui: 0.9% user + 0% kernel
  7. ...
  8. 100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait

*后一句表明了:

  1. 当是CPU占用100%, 满负荷了.
  2. 其中*大数是被iowait即I/O操作占用了.

此时分析方法调用栈, 一般来说会发现是方法中有频繁的文件读写或是数据库读写操作放在主线程来做了.

2.2.3 内存原因

其实内存原因有可能会导致ANR, 例如如果由于内存泄露, App可使用内存所剩无几, 我们点击按钮启动一个大图片作为背景的activity, 就可能会产生ANR, 这时trace信息可能是这样的:

  1. // 以下trace信息来自网络, 用来做个示例
  2. Cmdline: android.process.acore
  3. DALVIK THREADS:
  4. “main”prio=5 tid=3 VMWAIT
  5. |group=”main” sCount=1 dsCount=0 s=N obj=0x40026240self=0xbda8
  6. | sysTid=1815 nice=0 sched=0/0 cgrp=unknownhandle=-1344001376
  7. atdalvik.system.VMRuntime.trackExternalAllocation(NativeMethod)
  8. atandroid.graphics.Bitmap.nativeCreate(Native Method)
  9. atandroid.graphics.Bitmap.createBitmap(Bitmap.java:468)
  10. atandroid.view.View.buildDrawingCache(View.java:6324)
  11. atandroid.view.View.getDrawingCache(View.java:6178)
  12. MEMINFO in pid 1360 [android.process.acore] **
  13. native dalvik other total
  14. size: 17036 23111 N/A 40147
  15. allocated: 16484 20675 N/A 37159
  16. free: 296 2436 N/A 2732

可以看到free的内存已所剩无几.

当然这种情况可能更多的是会产生OOM的异常…

2.2 ANR的处理

针对三种不同的情况, 一般的处理情况如下

  1. 主线程阻塞的
    开辟单独的子线程来处理耗时阻塞事务.
  2. CPU满负荷, I/O阻塞的
    I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.
  3. 内存不够用的
    增大VM内存, 使用largeHeap属性, 排查内存泄露(这个在内存优化那篇细说吧)等.

3, 深入一点

没有人愿意在出问题之后去解决问题.
高手和新手的区别是, 高手知道怎么在一开始就避免问题的发生. 那么针对ANR这个问题, 我们需要做哪些层次的工作来避免其发生呢?

3.1 哪些地方是执行在主线程的

  1. Activity的所有生命周期回调都是执行在主线程的.
  2. Service默认是执行在主线程的.
  3. BroadcastReceiver的onReceive回调是执行在主线程的.
  4. 没有使用子线程的looper的Handler的handleMessage, post(Runnable)是执行在主线程的.
  5. AsyncTask的回调中除了doInBackground, 其他都是执行在主线程的.
  6. View的post(Runnable)是执行在主线程的.

3.2 使用子线程的方式有哪些

上面我们几乎一直在说, 避免ANR的方法就是在子线程中执行耗时阻塞操作. 那么在Android中有哪些方式可以让我们实现这一点呢.

3.2.1 启Thread方式

这个其实也是Java实现多线程的方式. 有两种实现方法, 继承Thread 或 实现Runnable接口:

继承Thread

  1. class PrimeThread extends Thread {
  2. long minPrime;
  3. PrimeThread(long minPrime) {
  4. this.minPrime = minPrime;
  5. }
  6. public void run() {
  7. // compute primes larger than minPrime
  8. . . .
  9. }
  10. }
  11. PrimeThread p = new PrimeThread(143);
  12. p.start();

实现Runnable接口

  1. class PrimeRun implements Runnable {
  2. long minPrime;
  3. PrimeRun(long minPrime) {
  4. this.minPrime = minPrime;
  5. }
  6. public void run() {
  7. // compute primes larger than minPrime
  8. . . .
  9. }
  10. }
  11. PrimeRun p = new PrimeRun(143);
  12. new Thread(p).start();

3.2.2 使用AsyncTask

这个是Android特有的方式, AsyncTask顾名思义, 就是异步任务的意思.

  1. private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
  2. // Do the long-running work in here
  3. // 执行在子线程
  4. protected Long doInBackground(URL... urls) {
  5. int count = urls.length;
  6. long totalSize = 0;
  7. for (int i = 0; i < count; i++) {
  8. totalSize += Downloader.downloadFile(urls[i]);
  9. publishProgress((int) ((i / (float) count) * 100));
  10. // Escape early if cancel() is called
  11. if (isCancelled()) break;
  12. }
  13. return totalSize;
  14. }
  15. // This is called each time you call publishProgress()
  16. // 执行在主线程
  17. protected void onProgressUpdate(Integer... progress) {
  18. setProgressPercent(progress[0]);
  19. }
  20. // This is called when doInBackground() is finished
  21. // 执行在主线程
  22. protected void onPostExecute(Long result) {
  23. showNotification("Downloaded " + result + " bytes");
  24. }
  25. }
  26. // 启动方式
  27. new DownloadFilesTask().execute(url1, url2, url3);

3.2.3 HandlerThread

Android中结合Handler和Thread的一种方式. 前面有云, 默认情况下Handler的handleMessage是执行在主线程的, 但是如果我给这个Handler传入了子线程的looper, handleMessage就会执行在这个子线程中的. HandlerThread正是这样的一个结合体:

  1. // 启动一个名为new_thread的子线程
  2. HandlerThread thread = new HandlerThread("new_thread");
  3. thread.start();
  4. // 取new_thread赋值给ServiceHandler
  5. private ServiceHandler mServiceHandler;
  6. mServiceLooper = thread.getLooper();
  7. mServiceHandler = new ServiceHandler(mServiceLooper);
  8. private final class ServiceHandler extends Handler {
  9. public ServiceHandler(Looper looper) {
  10. super(looper);
  11. }
  12. @Override
  13. public void handleMessage(Message msg) {
  14. // 此时handleMessage是运行在new_thread这个子线程中了.
  15. }
  16. }

3.2.4 IntentService

Service是运行在主线程的, 然而IntentService是运行在子线程的.
实际上IntentService就是实现了一个HandlerThread + ServiceHandler的模式.

以上HandlerThread的使用代码示例也就来自于IntentService源码.

3.2.5 Loader

Android 3.0引入的数据加载器, 可以在Activity/Fragment中使用. 支持异步加载数据, 并可监控数据源在数据发生变化时传递新结果. 常用的有CursorLoader, 用来加载数据库数据.

  1. // Prepare the loader. Either re-connect with an existing one,
  2. // or start a new one.
  3. // 使用LoaderManager来初始化Loader
  4. getLoaderManager().initLoader(0, null, this);
  5. //如果 ID 指定的加载器已存在,则将重复使用上次创建的加载器。
  6. //如果 ID 指定的加载器不存在,则 initLoader() 将触发 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以实现代码以实例化并返回新加载器
  7. // 创建一个Loader
  8. public Loader<Cursor> onCreateLoader(int id, Bundle args) {
  9. // This is called when a new Loader needs to be created. This
  10. // sample only has one Loader, so we don't care about the ID.
  11. // First, pick the base URI to use depending on whether we are
  12. // currently filtering.
  13. Uri baseUri;
  14. if (mCurFilter != null) {
  15. baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
  16. Uri.encode(mCurFilter));
  17. } else {
  18. baseUri = Contacts.CONTENT_URI;
  19. }
  20. // Now create and return a CursorLoader that will take care of
  21. // creating a Cursor for the data being displayed.
  22. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
  23. + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
  24. + Contacts.DISPLAY_NAME + " != '' ))";
  25. return new CursorLoader(getActivity(), baseUri,
  26. CONTACTS_SUMMARY_PROJECTION, select, null,
  27. Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
  28. }
  29. // 加载完成
  30. public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
  31. // Swap the new cursor in. (The framework will take care of closing the
  32. // old cursor once we return.)
  33. mAdapter.swapCursor(data);
  34. }

具体请参看官网Loader介绍.

3.2.6 特别注意

使用Thread和HandlerThread时, 为了使效果更好, 建议设置Thread的优先级偏低一点:

  1. Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);

因为如果没有做任何优先级设置的话, 你创建的Thread默认和UI Thread是具有同样的优先级的, 你懂的. 同样的优先级的Thread, CPU调度上还是可能会阻塞掉你的UI Thread, 导致ANR的.

结语

对于ANR问题, 个人认为还是预防为主, 认清代码中的阻塞点, 善用线程. 同时形成良好的编程习惯, 要有MainThread和Worker Thread的概念的…

MAC上使用androidstudio如何关联源码

一路cd 进去到你mac的这个目录下   ~/username/Library/Preferences/AndroidStudio2.X/options/jdk.table.xml,然后open it~~

%title插图%num

在sourcePath下面加入<root type=”simple” url=”jar://$USER_HOME$/Library/Android/sdk/sources/android-22/” />就可以了,当然啦我的是22,你们的可能是更高或者更低~~总之找到对应版本的sourcePath加上对应的这句话,就可以啦~~~,

 

添加:

有的AndroidStudio很顽固,就算像上面那样设置了,重启之后还是会刷新到以前的状态,现在有一个究*解决方案!!!

<jdk version=“2”>

        <name value=“Android API 22 Platform (1)” />

        <type value=“Android SDK” />

        <version value=“java version &quot;1.8.0_74&quot;” />

        <homePath value=“$USER_HOME$/Library/Android/sdk” />

        <roots>

            <annotationsPath>

                <root type=“composite”>

                    <root type=“simple” url=“jar://$APPLICATION_HOME_DIR$/plugins/android/lib/androidAnnotations.jar!/” />

                </root>

            </annotationsPath>

            <classPath>

                <root type=“composite”>

                    <root type=“simple” url=“jar://$USER_HOME$/Library/Android/sdk/platforms/android-22/android.jar!/” />

                    <root type=“simple” url=file://$USER_HOME$/Library/Android/sdk/platforms/android-22/data/res />

                </root>

            </classPath>

            <javadocPath>

                <root type=“composite” />

            </javadocPath>

            <sourcePath>

                <root type=“composite”>

                    <root type=“simple” url=file://$USER_HOME$/Library/Android/sdk/sources/android-22 />

                </root>

            </sourcePath>

        </roots>

        <additional jdk=“1.8” sdk=“android-22” />

    </jdk>

对的!!!就像上面一样,复制一个,名字后面加上(1)就可以了…目前还不知道什么原因,但是解决了..

Jenkins构建Android项目持续集成之系统配置

jenkins有自有的用户数据库,为了安全考虑,可以启用用户注册,使用权限控制登录。
安全设置
这里写图片描述
切记,授权策略要先选择“登录用户可以做任何事”,因为现在还没有任何用户,不能指定管理员,设置之后保存退出。
创建用户
注册
填写注册信息。

然后使用刚才注册的用户登录,”系统管理—>Configure Global Security”到安全管理界面
安全配置
将刚才的授权策略改为“安全矩阵”,在这个矩阵里,可以指定用户的权限,如上图。

注意:
如果忘记了管理员的密码怎么办呢?这里还有一个补救的办法。打开目录C:/<用户>/.jenkins/config.xml
config.xml
删掉上图红色圈圈的部分,即:

  1. <useSecurity>true</useSecurity>
  2. <authorizationStategy class=“hudson.sucrity.FullControlOnceLoggedInAuthorizationStrategy”>
  3. ……
  4. </authorizationStategy>
  5. <securityRealm class=“hudson.security.HudsonPrivateSecurityRealm”>
  6. <disableSignup>false</disableSignup>
  7. ..
  8. </securityRealm>

 

这样用户权限等于是初始化了,要重新配置。

系统设置

“系统管理—>系统设置”,进入到系统设置界面
系统设置界面
该页面,如果没有说到的就保持默认设置。

  1. Android sdk目录
    android sdk目录
  2. JDK目录
    JDK目录
  3. Git目录
    git目录
  4. Gradle 目录,使用Android Studio目录下的Gradle版本
    gradle目录
  5. Jenkins路径(要使用ip,不能用localhost)和管理员邮箱配置
    这里写图片描述
  6. 邮箱通知设置
    邮箱设置
    在点击测试的时候,可能会报错,大概意识是提示没有授权,原因是qq邮箱在使用SMTP发送邮箱时,需要开启一个设置。解决方法如下:
    登录要用来发送的qq邮箱
    这里写图片描述

这里写图片描述

开启SMTP的服务,之后会获取到一个授权码,将这个授权码填到上面的密码中。

*后保存即可。

注:用个人qq邮箱会需要授权问题,但使用企业qq邮箱时,直接填写邮箱密码即可,不知道其他邮箱会不会有这个问题。

总结

系统的配置就讲到这了,主要是配置一些工具的环境地址。下一篇将要开始讲如何创建一个项目、项目的配置、怎么让项目构建之后,将构建产物发给特定的人等等。

Unity 之 导出 Android项目过程简析

在出游戏包的时候,如果需要接入sdk,一般都不会直接在unity里操作,而是导出android工程,在android工程里操作,从unity5.x开始已经支持导出android studio工程

下面是Unity导出Android项目的过程,

配置BuildSetting,选择android平台点击switch platform,如果工程较大,时间会比较长。

Build Syatem 选择Gradle,否则导出的是eclipse工程

Explort Project 选中,否则打出来就是我们常用的apk形式

%title插图%num
打包之前记得设置报名,下图框中为默认名称,不修改发布会报错。

%title插图%num
若不修改包名,报错如下:

“UnityException: Bundle Identifier has not been set up correctly”

%title插图%num

设置后,点击export进行导出就可以了

导出后的工程目录如下:

%title插图%num

这就完成了Unity对Android项目的打包。*后我们打开android studio,导入后工程如果没有报错,就可以使用了。

%title插图%num

Unity5.x的版本也支持导出Ecplise的工程文件夹,,(后来出了Android Studio…,这个选项在Unity2017版本中被取消了)

如何用CRT工具连接服务器

如何用CRT工具连接服务器

首先打开CRT工具

%title插图%num

打开后右键点击sessions 选中quick connect
输入Hostname 例如:172.22.1.4
输入Username 例如:root
点击connect连接 输入密码可以将密码保存save

设置默认字符编码

%title插图%num

选择Options->sessionOptions->Terminal->apperance 修改character为utf-8

%title插图%num

 

2021年1—3月份全国规模以上工业企业利润同比增长1.37倍 两年平均增长22.6%

  1—3月份,全国规模以上工业企业实现利润总额18253.8亿元,同比增长1.37倍(按可比口径计算,详见附注二),比2019年1—3月份增长50.2%,两年平均增长22.6%。   1—3月份,规模以上工业企业中,国有控股企业实现利润总额6165.4亿元,同比增长1.99倍;股份制企业实现利润总额12792.7亿元,增长1.29倍;外商及港澳台商投资企业实现利润总额5128.1亿元,增长1.61倍;私营企业实现利润总额5163.3亿元,增长91.9%。   1—3月份,采矿业实现利润总额1519.5亿元,同比增长77.1%;制造业实现利润总额15366.3亿元,增长1.58倍;电力、热力、燃气及水生产和供应业实现利润总额1368.0亿元,增长56.9%。   1—3月份,在41个工业大类行业中,39个行业利润总额同比增加,1个行业扭亏为盈,1个行业实现减亏。主要行业利润情况如下:汽车制造业利润总额同比增长8.43倍,有色金属冶炼和压延加工业增长4.71倍,黑色金属冶炼和压延加工业增长3.88倍,化学原料和化学制品制造业增长3.43倍,电气机械和器材制造业增长1.67倍,专用设备制造业增长1.46倍,计算机、通信和其他电子设备制造业增长1.41倍,通用设备制造业增长1.19倍,煤炭开采和洗选业增长94.3%,非金属矿物制品业增长69.1%,电力、热力生产和供应业增长50.7%,纺织业增长40.4%,农副食品加工业增长28.9%,石油和天然气开采业增长18.4%。   1—3月份,规模以上工业企业实现营业收入27.48万亿元,同比增长38.7%;发生营业成本22.91万亿元,增长36.5%;营业收入利润率为6.64%,同比提高2.76个百分点。   3月末,规模以上工业企业资产总计128.70万亿元,同比增长9.5%;负债合计72.47万亿元,增长9.0%;所有者权益合计56.23万亿元,增长10.1%;资产负债率为56.3%,同比下降0.3个百分点。   3月末,规模以上工业企业应收账款16.59万亿元,同比增长17.1%;产成品存货4.73万亿元,增长8.5%。   1—3月份,规模以上工业企业每百元营业收入中的成本为83.37元,同比减少1.36元;每百元营业收入中的费用为8.60元,同比减少1.04元。   3月末,规模以上工业企业每百元资产实现的营业收入为86.0元,同比增加18.1元;人均营业收入为152.7万元,同比增加40.8万元;产成品存货周转天数为18.3天,同比减少4.7天;应收账款平均回收期为53.8天,同比减少10.2天。   3月份,规模以上工业企业实现利润总额7111.8亿元,同比增长92.3%。     表1 2021年1—3月份规模以上工业企业主要财务指标  分 组 营业收入 营业成本 利润总额 1-3月 同比增长 1-3月 同比增长 1-3月 同比增长 (亿元) (%) (亿元) (%) (亿元) (%) 总计 274828.0 38.7 229126.7 36.5 18253.8 137.3 其中:采矿业 10639.1 25.1 7365.0 19.7 1519.5 77.1    制造业 242118.0 41.2 202231.1 38.9 15366.3 157.8    电力、热力、燃气及水生产和供应业 22070.9 22.0 19530.6 21.4 1368.0 56.9 其中:国有控股企业 75508.7 32.8 60132.6 29.1 6165.4 199.4 其中:股份制企业 201385.0 38.2 167893.8 36.1 12792.7 129.2    外商及港澳台商投资企业 65303.2 42.1 54453.6 39.3 5128.1 160.9 其中:私营企业 104494.3 41.0 90035.4 39.8 5163.3 91.9 注: 1.经济类型分组之间存在交叉,故各经济类型企业数据之和大于总计。 2.本表部分指标存在总计不等于分项之和情况,是数据四舍五入所致,未作机械调整。  表2  2021年1—3月份规模以上工业企业经济效益指标  分 组 营业收入利润率 每百元营业收入中的成本 每百元营业收入中的费用 每百元资产实现的营业收入 人均营业收入 资产负债率 产成品存货周转天数 应收账款平均回收期 1-3月 1-3月 1-3月 3月末 3月末 3月末 3月末 3月末 (%) (元) (元) (元) (万元/人) (%) (天) (天) 总计 6.64 83.37 8.60 86.0 152.7 56.3 18.3 53.8 其中:采矿业 14.28 69.23 11.82 41.5 101.9 60.5 13.6 41.0    制造业 6.35 83.53 8.69 101.0 150.3 55.2 20.2 55.1    电力、热力、燃气及水生产和供应业 6.20 88.49 6.10 40.7 260.1 59.1 0.8 46.2 其中:国有控股企业 8.17 79.64 7.21 62.1 241.5 57.0 13.3 41.0 其中:股份制企业 6.35 83.37 8.79 82.7 149.8 57.1 18.9 51.4    外商及港澳台商投资企业 7.85 83.39 8.40 98.3 161.8 53.4 17.9 64.3 其中:私营企业 4.94 86.16 8.77 117.8 122.7 58.2 19.9 51.3  表3  2021年1—3月份规模以上工业企业主要财务指标(分行业)  行 业 营业收入 营业成本 利润总额 1-3月 同比增长 1-3月 同比增长 1-3月 同比增长 (亿元) (%) (亿元) (%) (亿元) (%) 总计 274828.0 38.7 229126.7 36.5 18253.8 137.3  煤炭开采和洗选业 5621.1 29.6 3787.9 22.0 808.8 94.3  石油和天然气开采业 1979.4 3.5 1244.6 2.5 411.2 18.4  黑色金属矿采选业 1230.1 57.0 929.1 46.5 143.5 780.4  有色金属矿采选业 635.7 29.0 439.8 21.1 96.8 95.2  非金属矿采选业 821.8 35.7 621.1 34.8 66.4 50.9  开采专业及辅助性活动 347.7 -6.9 339.5 -9.3 -7.2 (注1)  其他采矿业 3.3 153.8 3.0 150.0 0.1 (注2)  农副食品加工业 12121.4 26.0 10984.9 26.2 458.2 28.9  食品制造业 4891.6 23.2 3824.6 24.2 394.9 52.1  酒、饮料和精制茶制造业 4090.1 30.3 2568.7 28.2 767.5 44.1  烟草制品业 4287.7 13.0 1155.5 7.9 645.4 18.7  纺织业 5237.4 27.3 4654.6 26.8 168.8 40.4  纺织服装、服饰业 2968.2 16.5 2526.6 15.9 115.9 43.3  皮革、毛皮、羽毛及其制品和制鞋业 2320.2 17.7 2005.8 17.3 116.2 29.3  木材加工和木、竹、藤、棕、草制品业 2056.5 39.2 1859.5 39.9 64.0 27.2  家具制造业 1637.1 39.9 1374.3 40.1 67.3 72.6  造纸和纸制品业 3401.0 37.3 2871.2 35.9 247.6 112.7  印刷和记录媒介复制业 1535.4 30.8 1297.3 31.0 71.2 49.3  文教、工美、体育和娱乐用品制造业 3011.0 35.6 2623.1 35.7 124.5 57.0  石油、煤炭及其他燃料加工业 12089.9 19.6 9640.1 6.6 899.1 (注3)  化学原料和化学制品制造业 17606.8 40.2 14349.5 32.8 1688.9 342.5  医药制造业 6555.1 33.5 3594.4 26.1 1147.6 88.7  化学纤维制造业 2153.0 40.7 1895.8 34.7 136.6 596.9  橡胶和塑料制品业 6341.3 44.0 5305.4 41.4 396.9 140.0  非金属矿物制品业 12996.2 40.0 10849.9 39.2 857.6 69.1  黑色金属冶炼和压延加工业 20483.1 50.5 18837.3 47.7 871.4 387.6  有色金属冶炼和压延加工业 14388.2 43.0 13269.1 39.2 555.0 471.0  金属制品业 9325.1 49.2 8200.3 48.6 366.6 116.7  通用设备制造业 9873.3 49.4 8111.6 48.0 633.2 118.7  专用设备制造业 8253.4 49.8 6467.1 47.2 707.6 145.5  汽车制造业 21162.7 71.5 18018.9 69.4 1320.8 843.4  铁路、船舶、航空航天和其他运输设备制造业 2534.1 37.8 2162.3 37.9 103.1 72.4  电气机械和器材制造业 16763.1 56.5 14325.8 56.1 838.0 166.7  计算机、通信和其他电子设备制造业 29956.9 39.6 26016.4 37.6 1383.5 141.4  仪器仪表制造业 1764.4 46.1 1330.8 43.2 140.5 111.0  其他制造业 431.8 41.8 364.3 41.1 20.7 80.0  废弃资源综合利用业 1601.4 90.9 1503.6 92.8 52.2 99.2  金属制品、机械和设备修理业 280.6 5.3 242.4 2.1 5.3 194.4  电力、热力生产和供应业 18261.3 21.2 16309.7 20.9 1080.9 50.7  燃气生产和供应业 2974.5 26.2 2599.3 25.1 227.2 66.1  水的生产和供应业 835.1 26.2 621.6 21.6 59.8 234.1 注: 1.开采专业及辅助性活动上年同期亏损15.4亿元。 2.其他采矿业上年同期利润总额为0.0亿元。 3.石油、煤炭及其他燃料加工业上年同期亏损252.4亿元。 4.本表部分指标存在总计不等于分项之和情况,是数据四舍五入所致,未作机械调整。    附注:   一、指标解释及相关说明   1、利润总额:指企业在生产经营过程中各种收入扣除各种耗费后的盈余,反映企业在报告期内实现的盈亏总额。   2、营业收入:指企业从事销售商品、提供劳务和让渡资产使用权等生产经营活动形成的经济利益流入。包括主营业务收入和其他业务收入。   3、营业成本:指企业从事销售商品、提供劳务和让渡资产使用权等生产经营活动发生的实际成本。包括主营业务成本和其他业务成本。营业成本应当与营业收入进行配比。   4、资产总计:指企业过去的交易或者事项形成的、由企业拥有或者控制的、预期会给企业带来经济利益的资源。   5、负债合计:指企业过去的交易或者事项形成的、预期会导致经济利益流出企业的现时义务。   6、所有者权益合计:指企业资产扣除负债后由所有者享有的剩余权益。   7、应收账款:指资产负债表日以摊余成本计量的,企业因销售商品、提供服务等经营活动应收取的款项。   8、产成品存货:指企业报告期末已经加工生产并完成全部生产过程、可以对外销售的制成产品。   9、营业收入利润率=利润总额÷营业收入×100%,单位:%。   10、每百元营业收入中的成本=营业成本÷营业收入×100,单位:元。   11、每百元营业收入中的费用=(销售费用+管理费用+研发费用+财务费用)÷营业收入×100,单位:元。   12、每百元资产实现的营业收入=营业收入÷平均资产÷累计月数×12×100,单位:元。   13、人均营业收入=营业收入÷平均用工人数÷累计月数×12,单位:万元/人。   14、资产负债率=负债合计÷资产总计×100%,单位:%。   15、产成品存货周转天数=360×平均产成品存货÷营业成本×累计月数÷12,单位:天。   16、应收账款平均回收期=360×平均应收账款÷营业收入×累计月数÷12,单位:天。   17、两年平均增速是指以2019年相应同期数为基数,采用几何平均的方法计算的增速。   18、在各表的利润总额同比增长栏中,标“注”的表示上年同期利润总额为负数,即亏损;数值为正数的表明利润同比增长;数值在0至-100%之间(不含0)的表明利润同比下降;下降幅度超过100%的表明由上年同期盈利转为本期亏损;数值为0的表明利润同比持平。   二、规模以上工业企业利润总额、营业收入等指标的增速均按可比口径计算。报告期数据与上年所公布的同指标数据之间有不可比因素,不能直接相比计算增速。其主要原因是:(一)根据统计制度,每年定期对规模以上工业企业调查范围进行调整。每年有部分企业达到规模标准纳入调查范围,也有部分企业因规模变小而退出调查范围,还有新建投产企业、破产、注(吊)销企业等变化。(二)加强统计执法,对统计执法检查中发现的不符合规模以上工业统计要求的企业进行了清理,对相关基数依规进行了修正。(三)加强数据质量管理,剔除跨地区、跨行业重复统计数据。   三、统计范围   规模以上工业企业,即年主营业务收入为2000万元及以上的工业法人单位。   四、调查方法   规模以上工业企业财务状况报表按月进行全面调查(1月份数据免报)。   五、行业分类标准   执行国民经济行业分类标准(GB/T4754-2017),具体请参见http://www.stats.gov.cn/tjsj/tjbz/hyflbz/。 

查看服务器连接数

netstat -na | grep ESTAB | grep 8080 | wc -l //统计tomcat并发连接数;

 

netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’//查看服务器tcp并发连接数

netstat -nat|grep 80 //查看nginx或者tomcat tcp连接

 

netstat -nat |awk ‘{print $5}’|awk -F: ‘{print $4}’|sort|uniq -c|sort -nr|head -10

简单服务器和客户端的连接

###服务器

# -*- coding: utf-8 -*-
import socket
import threading
from docutils.parsers.rst.directives import encoding
#进行封装。*个:服务器之间网络通信,第二个:流式socket,for TCP
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=(‘192.168.1.35’,985) #设置服务器IP
s.bind(ip_port) #服务器绑定IP
s.listen(5) #设置监听位数
con,address=s.accept() #服务器进行接收数据
print(‘%s have connected’ % address[0])
con.send(‘hello i am python’.encode())

isok=True
def rec(con):
global isok
while isok:
recv_data=str(con.recv(1024),encoding=’utf-8′)
if recv_data==’exit’:
isok=False
print(recv_data)
thrd=threading.Thread(target=rec,args=(con,))
thrd.start()
while isok:
send_d=input(‘server>’)
con.sendall(bytes(send_d,encoding(‘utf-8′)))
if send_d==’exit’:
isok=False

s.close()

##客户端

# -*- coding: utf-8 -*-
import socket
import threading

cl=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=(‘192.168.1.35’,985)
cl.connect(ip_port)

isok=True
def rec(cl):
global isok
while isok:
t=cl.recv(1024).decode(‘utf-8′)
if t==’exit’:
isok=False
print(t)

th2=threading.Thread(target=rec,args=(cl,))
th2.start()

while isok:
t=input(“other>”)
cl.send(t.encode(‘utf-8′))
if t==’exit’:
isok=False

cl.close()
%title插图%num %title插图%num

linux免密登录远程服务器

网上的教程都是windows,xshell为主。但是我一般笔记本不想用windows,有需要免密登录服务器,那咋么办呢?
进过了大概两年的摸索(因为之前用很愚蠢的方法登录上去了。。。。。)
这里记录一下主要为了给之后其他人看。

登录服务器
先让管理员设置一下允许密码登录,然后登录服务器,可以参考我之前写的lnux下远程连接服务器汇总
我服务器的用户名是rhf是蓝色,本地是是绿色,以作区分。

设置ssh
之后我们看一下有没有.ssh 文件
%title插图%num

这样子是有的,如果没有的话,运行ssh-keygen, 你可以看到会你的主用户目录有一个.ssh,下面有两个文件

%title插图%num

cat ~/.id_rsa.pub >> ~/.ssh/authorized_keys
chmod 600 .ssh/authorized_keys
chmod 700 -R .ssh
1
2
3
配置免密
我们这两个文件改成id_rsa_rhf_106, id_rsa_rhf_106.pub,然后下载下来保存到我们本地,如何下载可以参考上面的链接,或者用FileZilla这个软件。

下载下来之后我们将这两个文件复制到本地的.ssh目录下。
在服务器上新建一个文件
$ touch ~/.ssh/authorized_keys
1
然后将id_rsa_rhf_106.pub的内容复制到authorized_keys里面
到这里大功告成,你发现你现在登录可以不用输入密码了

配置快捷键
在本地新建文件

$ touch ~/.ssh/config

内容如下,如果你要添加的话,复制几个就好了

Host 106i
HostName 192.168.X.XXX
User rhf
Port 22

106i是我取得名字,你可以自己定义
做好这些以后,我们只需要再终端中输入

$ ssh 106i

就可以登录服务器了

生成.ppk文件
有些文件传输,不输入密码,需要.ppk文件才行。

%title插图%num

比如这里的key file就需要一个.ppk文件。

$ puttygen id_rsa -o putty2.ppk

即可

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速