Android 多进程编程 15问15答!
ps:阅读本文 需要对android 多进程编程有一定了解。
1.Android中总共有几种方式进行IPC?
答:一共有两种,一种是binder 还有一种是socket。Binder 大家用的比较多。Socket很少有人用,这里给出一个利用Socket进行ipc通信的例子。
服务端代码:
package com.example.administrator.socketipcdemo; import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log; import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket; public class ServerService extends Service { public static final int SERVER_PORT_NUMBER = 5678;
private boolean mServiceDestroyed = false; @Override
public void onCreate() {
new Thread(new ServerRunnable()).start();
super.onCreate();
} @Override
public void onDestroy() {
mServiceDestroyed = true;
super.onDestroy();
} public ServerService() {
} @Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
} //这个线程 用于监听端口号
class ServerRunnable implements Runnable {
@Override
public void run() { ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(SERVER_PORT_NUMBER);
} catch (IOException e) {
e.printStackTrace();
}
while (!mServiceDestroyed) {
try {
final Socket client = serverSocket.accept();
new Thread() {
@Override
public void run() {
try {
responseClientRequest(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start(); } catch (IOException e) {
e.printStackTrace();
}
}
}
} private void responseClientRequest(Socket client) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
while (!mServiceDestroyed) {
String str = in.readLine();
if (str == null) {
break;
}
String reponseStr = "我是服务器端,我收到了来自客户端的消息:" + str;
out.println(reponseStr);
}
out.close();
in.close();
client.close();
}
}
客户端代码:
package com.example.administrator.socketipcdemo; import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket; public class MainActivity extends AppCompatActivity { private Button bt, bt2;
private TextView tv;
private EditText et; private Socket socket;
private PrintWriter mPrintWriter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, ServerService.class);
startService(intent);
tv = (TextView) findViewById(R.id.tv);
et = (EditText) findViewById(R.id.et);
bt = (Button) this.findViewById(R.id.bt);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
tryToLinkServer();
}
}.start(); }
}); bt2 = (Button) this.findViewById(R.id.bt2);
bt2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendMessageToServer(et.getText().toString());
}
}); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
} @Override
protected void onDestroy() {
if(socket!=null)
{
try {
socket.shutdownInput();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId(); //noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
} return super.onOptionsItemSelected(item);
} private void sendMessageToServer(final String str) {
if (mPrintWriter != null) {
mPrintWriter.println(str);
}
} private void tryToLinkServer() {
while (socket == null) {
try {
socket = new Socket("localhost", ServerService.SERVER_PORT_NUMBER);
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
//每次连不上就等待1秒再重新连
SystemClock.sleep(1000);
e.printStackTrace();
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "和服务器进程成功链接", Toast.LENGTH_SHORT).show();
}
});
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!MainActivity.this.isFinishing()) {
final String msg = br.readLine();
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText(msg);
}
});
}
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
最后看一下运行效果:
2.android:process 属性值的书写有什么讲究?
答::remote 这种写法 其实就等于 包名:remote 而且这种写法 属于当前app的私有进程, 其他app 不可以和这个进程跑在一起的。
如果是直接 把android:process 写成字符串形式的话, 那这种进程就是全局进程了,谁都可以用。哪怕是其他app 只要是shareUid一样
就可以和这个进程跑在一起。当然了,签名也必须一样!
3.多进程编程的时候 我们需要注意哪些坑?
答:一定要记住 多进程的时候 实际上 一个进程就是一个虚拟机,所以 会引发如下几个问题:
a:所有静态变量和单例模式之类的 全部没用了。因为你多进程么,假设你的app代码里面 开启了2个进程,同时你的代码里面 有一个静态变量,这个时候注意 你的静态变量虽然在代码里只有一份,
但是此时是2个进程在跑 ,有2个虚拟机,所以你这个静态变量也是会变成两份的!
b:SharedPreferences 会失去同步效果。因为sp这个东西 你们看下源码就知道了,实际上他的底层就是个xml。。。并且android 对这个xml的读写自己弄了一套缓存,注意这个缓存是运行在内存中的,
所以你就知道 一旦多个进程的话,这个缓存就是多份了。。。。所以记住 不要在多进程里面使用sp
c:Application 会创建多次。这个你们打下log就知道了,所以如果你们application 有特殊处理的话 记得考虑下多进程的情况。
上面3个是我们需要注意的点,其实也很好理解,对于同一个app的 多个进程来说 ,他就等于 多个app 在运行,只不过这些app的uid和签名都一样!你想明白这点 就懂了android 多进程了。
4.android中有哪几种序列化方式?分别要注意什么?
答:两种。Serializable和Parcelable
Serializable:这种方式 就是要注意serialVersionUID,一般ide 给你生成的值 都是根据你class的结构来做的,假设你在版本1.0.1 里面 创建了class A,并且序列化了一个对象a 在sd卡里。
过了一段时间 你在1.0.2 版本里 把A的代码 改了很多,此时ide 会把你的serialVersionUID 值也随之改掉。那如果你在1.0.2版本里 还想反序列化这个对象a的话 就会出错了。
所以serialVersionUID 就是一个记录class 版本号的标示,我们最好还是把他写死。。。。这样不会影响我们的客户端。此外 transient 这个标记的变量也不会被序列化。
Parcelable:write方法就是序列化的时候 执行的方法 creator 就是反序列化的过程。Intent bundle bitmap 都实现了这个接口。List Map 也可以序列化 提前是 元素是这个接口对象。
两者基本上是差不多的,只不过Parcelable效率更高,一般在内存中使用时 我们用Parcelable,如果是 序列化到本地或者网络 这种流里的时候 我们就用Serializable 因为比较方便。
5.当遇到一个数据 无法放在Bundle里时,如何进行进程间通讯?
答:假设我们有一个需求,让A进程 计算一个东西以后 把 结果 result 传递给B进程。但是这个result结果 没有实现Parcelable 接口也无法放在bundle里。此时 该如何传递这个result到B?
其实可以让A进程 启动B进程 的一个intentService。让这个service 去计算出来这个结果result,这时候 我们会发现 此时这个service和b进程 在一个进程里。 result当然就随便传递了。不需要ipc来传递
6.Messenger 使用时,要注意哪些,他的原理是什么?
答:原理的话 其实看一下 源码就知道了:
/**
* Create a Messenger from a raw IBinder, which had previously been
* retrieved with {@link #getBinder}.
*
* @param target The IBinder this Messenger should communicate with.
*/
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
一看到这个代码我们就明白 原理就是AIDL么。要注意的话 就是Messenger只能用于单向通信,不能用于双向的 。如果你要用双向通信并且还要用Messenger来完成的话 就必须用2个Messenger。
Messenger传递对象就是传递的Message对象。其中我们注意了,在进程通信的时候,Message里的object 字段 只能传递 系统的parceable对象。也就是intent bitmap 这种系统自己实现的parceable接口对象。
如果是我们自己写的parceable 是无法放在object里面 用于进程通信的,如果你要用,请放在bundle里。此外Messenger 在内部 是串行处理请求的,大量消息 一起发给服务端这种业务模型
最好就别用Messenger了。直接自己写一个AIDL 最合适。
7.使用AIDL的时候 有哪些注意事项?
答:有下面几个注意事项:
a:aidl文件里不是所有数据类型都可以使用。只支持 基本数据类型,string,charSequence,List(只支持ArrayList,且里面的元素都能被AIDL支持),Map(只支持hashMap,并且元素必须是AIDL支持的),Parcelable,AIDL自己。
b:自定义的parceable对象要在aidl中 import 进来。并且自定义的parceable 要在aidl里 也额外声明一下,具体的看我前面的那篇blog即可。
c:aidl 里面只能写方法 不能写静态常量。
8.in out inout 这三个参数 在aidl 到底有啥用,不写有什么危害么?
答:据我观察 你不写其实没什么危害,但是你写明白了 传输效率会增加。其实这3个参数很好理解,简单建立一个方法模型:
假设你的aidl文件里有一个方法 void addPerson(Person person),这方法一看 就是客户端传一个person 到服务端 所以这个地方参数就是in。
如果你有一个方法叫 void getList(List a) 你看这个方法 就是把服务端的list 传递给这个参数a的list 那你就把参数写成out即可。
inout 就更好理解了,实际就是 void modifyList(List b),意思就是 先把b 这个list 传递给服务端,然后还要修改这个list。
9.CopyOnWriteArrayList 可以在aidl中使用吗?为什么?
答:可以使用。
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
虽然看声明 他并没有实现Parceable这个接口。也不是ArrayList的子类,但实际上AIDL支持的是抽象的List,所以在底层的时候 Binder 会把CopyOnWriteArrayList
转换成ArrayList 传递给客户端的。ConcurrentHashMap 也是一样。
10.Android 进程通信时候 能传递对象吗?
答:严格意义上 是不能的,实际上binder 会把客户端传递过来的对象 重新转换为一个新的对象 传递给服务端。对象的跨进程传输 本质上就是序列化和反序列化的过程。
11.RemoteCallBackList 干嘛的?使用时注意哪些?
答:具体可看http://www.cnblogs.com/punkisnotdead/p/5158016.html。 实际上这个东西讲明白了 就是用于 android中管理 跨进程listener接口的。
你想明白这个道理 就都懂了。而且他特别方便的是,客户端进程终止以后 他可以自动删除listenr。使用注意点就是begin和finish要成对出现。就算你是遍历listenrer操作
也要用这2个方法
12.aidl运行时的 线程模型?
答:http://www.cnblogs.com/punkisnotdead/p/5163464.html 这篇分析binder代码的时候 已经讲的很清楚了。这里再总结一次。
client 调用 server的方法a,client本身是会挂起的, 会一直等待server的方法a的 执行结果。
同时这个方法a 是运行在binder线程池中,所以server的方法 可以执行耗时操作。只不过 client调用的server方法时
注意不要在ui线程里调用,不然会anr的。同时如果你的业务是 一个server 多种client ,那你的server端 方法 要支持同步,不然会数据错乱的!
13.服务端 调用 客户端listener 方法有什么讲究?
答:实际上 和12问题是一样的,server 想调用 client的方法b,b也是会执行在 client所属进程的 binder线程池内的。同时我们也要避免 如果方法b执行时间很长,
记得server调用的时候 不要在主线程里调用。此外我们一般client 都是在activity里,记得 listener调用的方法 如果涉及到ui,请用handler切换到ui线程来做。
14.如何做权限认证?
答:两种方法,可以在onBind里 检查权限,也可以在server的onTransact里验证,验证失败返回false即可。后者方法更好 还可以验证uid pid等 可以支持复杂的多进程业务模型,
尤其是安全相关的。
15.一个aidl 就必须对应一个service吗?多种aidl 是不是只能多个service?
答:不是的,aidl 只不过是帮你书写binder用的工具,你当然可以只启动一个service 但是对应多个binder。要知道 service 资源是有限的 假设你app复杂 10个业务 开启10个service
那不是就乱套了。完全可以只开启一个service 但是这一个service 可以控制多种aidl 对应的binder,你要用哪个 就取那个binder 即可。
下面给出实例。
首先看一下 代码结构和运行效果,实际上就是一个加法binder 和一个乘法binder 只不过 这2个binder 都由一个service来控制而已。
然后来看一下加法和乘法的 aidl以及对应的binder 对象:
// IAddition.aidl
package com.example.administrator.bindermanager; // Declare any non-default types here with import statements interface IAddition { int add(int a,int b);
}
package com.example.administrator.bindermanager; import android.os.RemoteException; /**
* Created by Administrator on 2016/1/29.
*/
public class AdditionImpl extends IAddition.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
// IMultiplication.aidl
package com.example.administrator.bindermanager; // Declare any non-default types here with import statements interface IMultiplication { int multip(int a,int b);
}
package com.example.administrator.bindermanager; import android.os.RemoteException; /**
* Created by Administrator on 2016/1/29.
*/
public class MultiplicationImpl extends IMultiplication.Stub {
@Override
public int multip(int a, int b) throws RemoteException {
return a * b;
}
}
然后看看我们最为关键的binder manger 如何设计:
// IBinderManger.aidl
package com.example.administrator.bindermanager; // Declare any non-default types here with import statements interface IBinderManger {
IBinder queryBinder(int binderCode);
}
package com.example.administrator.bindermanager; import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log; /**
* Created by Administrator on 2016/1/29.
*/
public class BinderMangerImpl extends IBinderManger.Stub { public BinderMangerImpl() {
super();
} @Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BinderManger.BINDER_ADDITION:
binder = new AdditionImpl();
break;
case BinderManger.BINDER_MULTIPLICATION:
binder = new MultiplicationImpl();
break;
default:
break;
}
return binder;
}
}
package com.example.administrator.bindermanager; import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log; public class BinderMangerService extends Service { private IBinder mBinderManger = new BinderMangerImpl(); public BinderMangerService() {
} @Override
public IBinder onBind(Intent intent) {
return mBinderManger;
}
}
package com.example.administrator.bindermanager; import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException; /**
* Created by Administrator on 2016/1/29.
*/
public class BinderManger { public static final int BINDER_ADDITION = 0;
public static final int BINDER_MULTIPLICATION = 1;
private Context mContext;
private IBinderManger mBinderManger;
private static BinderManger sInstance; private BinderManger(Context mContext) {
this.mContext = mContext.getApplicationContext();
connectBinderService();
} public static BinderManger getInstance(Context context) {
if (sInstance == null) {
sInstance = new BinderManger(context);
}
return sInstance;
} private synchronized void connectBinderService() {
Intent service = new Intent(mContext, BinderMangerService.class);
mContext.bindService(service, mServiceConnection, Context.BIND_AUTO_CREATE);
} private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderManger = IBinderManger.Stub.asInterface(service);
} @Override
public void onServiceDisconnected(ComponentName name) { }
}; public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
binder = mBinderManger.queryBinder(binderCode);
return binder;
}
}
最后看一下 我们的clinet端:
package com.example.administrator.bindermanager; import android.os.Bundle;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView; import org.w3c.dom.Text; public class MainActivity extends AppCompatActivity { private Button bt, bt2, bt3;
private EditText et1, et2;
private TextView tv;
private BinderManger binderManger; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv2);
et1 = (EditText) findViewById(R.id.et);
et2 = (EditText) findViewById(R.id.et2);
bt = (Button) this.findViewById(R.id.bt);
bt.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
binderManger = BinderManger.getInstance(MainActivity.this);
}
}); bt2 = (Button) this.findViewById(R.id.bt2);
bt2.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
try {
final IMultiplication multiplicationImpl =
(IMultiplication) MultiplicationImpl.asInterface(binderManger.queryBinder(BinderManger.BINDER_MULTIPLICATION)); runOnUiThread(new Runnable() {
@Override
public void run() {
try {
tv.setText(multiplicationImpl.multip(Integer.parseInt(et1.getText().toString()), Integer.parseInt(et2.getText().toString())) + "");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}); } catch (RemoteException e) {
e.printStackTrace();
}
}
}); bt3 = (Button) this.findViewById(R.id.bt3);
bt3.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
try {
final IAddition additionImpl =
(IAddition) AdditionImpl.asInterface(binderManger.queryBinder(BinderManger.BINDER_ADDITION));
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
tv.setText(additionImpl.add(Integer.parseInt(et1.getText().toString()), Integer.parseInt(et2.getText().toString())) + "");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}); } catch (RemoteException e) {
e.printStackTrace();
}
}
}); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId(); //noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
} return super.onOptionsItemSelected(item);
}
}
Android 多进程编程 15问15答!的更多相关文章
- Android 线程与消息 机制 15问15答
1.handler,looper,messagequeue三者之间的关系以及各自的角色? 答:MessageQueue就是存储消息的载体,Looper就是无限循环查找这个载体里是否还有消息.Handl ...
- Android View绘制13问13答
1.View的绘制流程分几步,从哪开始?哪个过程结束以后能看到view? 答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程.draw ...
- 《编程人生:15位软件先驱访谈录》【PDF】下载
<编程人生:15位软件先驱访谈录>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382231 内容简介 本书适合所有程序员,也适合 ...
- Android BaseAdapter加载多个不同的Item布局时出现UncaughtException in Thread main java.lang.ArrayIndexOutOfBoundsException: length=15; index=15
java.lang.ArrayIndexOutOfBoundsException: length=15; index=15 异常出现的场景:在做聊天界面时,需要插入表情,图片,文字,名片,还有几种较为 ...
- 并发编程学习笔记(15)----Executor框架的使用
Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...
- Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航
原文:Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航 经过前面的学习,Android Studio开发环境已准备OK,运行Android应用程序的原生模拟器和Ge ...
- 《Qt on Android核心编程》介绍
<Qt on Android核心编程>最终尘埃落定.付梓印刷了. 2014-11-02更新:china-pub的预售链接出来了.折扣非常低哦. 封面 看看封面的效果吧,历经几版,最终就成了 ...
- Java 面试题问与答:编译时与运行时
Java 面试题问与答:编译时与运行时 2012/12/17 | 分类: 基础技术, 职业生涯 | 5 条评论 | 标签: RUNTIME, 面试 分享到:58 本文作者: ImportNew - 朱 ...
- Android 网络编程 记录
简单介绍 看了深入理解Android网络编程感觉不错.今天对Android网络编程进行了要点记录. 内容 Android基于网络技术和编程实践 要点 定义 描写叙述 IP协议 用于报文交换网络的一种面 ...
随机推荐
- loadrunner之Paramater在负载测试中的数据生成规则
前段时间在做性能测试的时候,基于业务的需求,使用到了Unique Number的参数类型. 脚本的业务是注册以alien开头,后面接数字的用户帐号,填写相关帐号信息.提交企业信息进行审核. 其中用户帐 ...
- qt之esc键
Esc键对大家来说实在熟悉不过的了,在Qt中Esc键也会默认的进行一些事件的触发,今天对Esc键测试了一下,突然发现不像我想象的那样,在QDialog中按下Esc键会默认调用reject()方法而不是 ...
- MyBatis学习总结_05_实现关联表查询
一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
- swift:创建表格UITableView
用swift创建单元格和用iOS创建单元格形式基本相同,就是语法上有些异样.swift中调用成员方法不再使用[ ]来发送消息,而是使用.成员方法的形式调用成员函数.这种格式非常类似于java中的点成员 ...
- C++时间戳转化(涉及GMT CST时区转化)
问题由来 时间戳转换(时间戳:自 1970 年1月1日(00:00:00 )至当前时间的总秒数.) #include <stdio.h> #include <time.h> i ...
- 卷积神经网络(CNN)
1. 概述 卷积神经网络是一种特殊的深层的神经网络模型,它的特殊性体现在两个方面,一方面它的神经元间的连接是非全连接的, 另一方面同一层中某些神经元之间的连接的权重是共享的(即相同的).它的非全连接和 ...
- linux怎么运行.SH文件
执行sh xx.sh命令就可以执行.sh文件了.如果直接执行xx.sh文件,就报权限错误 解决办法:执行chmod u+x xx.sh 来添加执行权限
- adobeMediaServer
参考地址: http://www.piis.cn/news/new519.asp http://www.cuplayer.com/player/PlayerCode/RTMP/2012/0918427 ...
- spring mvc 国际化
spring mvc 国际化 使用CookieLocaleResolver实现国际化的步骤:1.注册 messageSource,localeResolver 两个bean2.调用localeReso ...
- iOS 7 如何关闭已打开的应用(App)
刚升级了 iOS 7,感觉不太会用了. 在多任务状态下,看着一个个已被打开的应用,不知道如何关闭了. 问了下朋友才知道,在多任务状态下,将对应的应用 向上划 就行. 听说,和 Android 一样的 ...