AndroidService 深度分析(2)

AndroidService 深度分析(2)

上一篇文章我们Service的生命周期进行了測试及总结。

这篇文章我们介绍下绑定执行的Service的实现。

绑定执行的Service可能是仅为本应用提供服务,称为本地Service。也可能为其它应用提供跨进程服务,即远程Service。以下分别进行介绍:

本地Service

假设Service仅仅服务于本应用。那么我们仅仅须要继承Binder类,定义我们须要实现的方法就可以,当发起绑定连接时,Service将会在onBind方法中返回这个继承类的对象。使得client与Service共享一个Binder对象。Binder就像一座桥梁,使client与Service可以互相联系。以下贴出本地Service的实现演示样例:

LocalService代码:

public classLocalService extends Service {private String TAG =getClass().getSimpleName();MyBinder myBinder = new MyBinder();ServiceListener myServiceListener;public LocalService() {}public interface ServiceListener {public String getActivityInfo();}private void setListener(ServiceListenermyServiceListener) {this.myServiceListener = myServiceListener;}//绑定成功后。Service就能够通过这种方法获得Activity的信息private void getActivityInfo() {String activityInfo =myServiceListener.getActivityInfo();Log.d(TAG, TAG +"+activityInfo------>" + activityInfo);}private String getInfo() {return "Hello,我是LocalService的方法,你能够通过它的对象訪问我。";}public class MyBinder extends Binder {public String getServiceInfo() {return getInfo();}public void setServiceListener(ServiceListenermyServiceListener) {setListener(myServiceListener);}}@Overridepublic IBinder onBind(Intent intent) {Log.d(TAG, TAG +"------>onBind()");return myBinder;}@Overridepublic void onRebind(Intent intent) {Log.d(TAG, TAG +"------>onRebind()");super.onRebind(intent);}@Overridepublic boolean onUnbind(Intent intent) {Log.d(TAG, TAG +"------>onUnbind()");//return false;这里的返回值决定下一次绑定时是否运行onRebindreturn true;}
}

LocalActivity代码:

public classLocalActivity extends ActionBarActivity {private String TAG =getClass().getSimpleName();Intent serviceIntent;@Overrideprotected void onCreate(BundlesavedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_local);Log.d(TAG, TAG +"------>onCreate()");serviceIntent = new Intent(this,LocalService.class);}public void bindService(View v) {Log.d(TAG, TAG +"------>bindService()");bindService(serviceIntent,serviceConnection, Service.BIND_AUTO_CREATE);}public void unBindService(View v) {Log.d(TAG, TAG +"------>unBindService()");unbindService(serviceConnection);}@Overrideprotected void onDestroy() {Log.d(TAG, TAG +"------>onDestroy()");super.onDestroy();}ServiceConnection serviceConnection = newServiceConnection() {@Overridepublic voidonServiceConnected(ComponentName name, IBinder service) {Log.d(TAG, TAG +"------>onServiceConnected()");LocalService.MyBinder binder =(LocalService.MyBinder) service;String localServiceInfo =binder.getServiceInfo();Log.d(TAG, TAG +"+onServiceConnected------>" + localServiceInfo);binder.setServiceListener(newLocalService.ServiceListener() {@Overridepublic String getActivityInfo(){return "Hello,我在LocalActivity中。LocalService能够调用我获得LocalActivity的消息!";}});}@Overridepublic voidonServiceDisconnected(ComponentName name) {Log.d(TAG, TAG +"------>onServiceDisconnected()");}};
}

Activity相应的布局就是两个button。分别实现绑定和解绑功能,如图:

Activity与Service都是须要在Manifest文件里注冊的哦。

 

我们启动Activity。先后绑定Service,输出日志例如以下:

03-17 10:10:58.525  D/LocalActivity﹕LocalActivity------>onCreate()
03-17 10:11:00.955  D/LocalActivity﹕LocalActivity------>bindService()
03-17 10:11:00.975  D/LocalService﹕LocalService------>onBind()
03-17 10:11:00.995  D/LocalActivity﹕LocalActivity------>onServiceConnected()
03-17 10:11:00.995  D/LocalActivity﹕ LocalActivity+onServiceConnected------>Hello,我是LocalService的方法。你能够通过它的对象訪问我。
03-17 10:11:16.345  D/LocalActivity﹕LocalActivity------>unBindService()
03-17 10:11:16.345  D/LocalService﹕LocalService------>onUnbind()

       上面的日志显示,我们的确实现了Service的绑定与解绑工作,不仅如此,细心的你应该还发现了我们实现了Service与Activity中相互的调用吧。是的。在实际工作中我们不仅须要指示Service为我们提供服务。Service有时也须要获取到client的数据来更好地提供服务(LocalService中的getActivityInfo方法  通过回调实现)。

这里我总结下详细的实现过程:

1、在Service类中,设计继承Binder类的内部类MyBinder,加入须要向Activity提供的方法。本例中的getServiceInfo方法实现了获取Service信息的功能,当然有时候为了简便。我们直接提供方法返回Service对象。可是一般并不建议这样做;同一时候注意到setServiceListener方法。它是实现Service调用Activity提供方法的重要环节。我们通过回调的方法实现了Service对Activity的訪问。

2、重写onBind方法,并返回MyBinder对象。至此。Service类的设计就完毕了;

3、在Activity中。重写ServiceConnection接口的onServiceConnected与onServiceDisConnected方法。在onServiceConnected方法中。我们获得了onBinder方法返回的MyBinder对象。然后调用setServiceListener方法设置Service訪问Activity所须要的回掉接口对象;
4、至此,Service与Activity之间的 “桥梁”搭建完成。Service中我们能够通过getActivityInfo方法获得Activity的信息;而在Activity中,我们也能够通过getServiceInfo方法获得Service的信息。

远程Service

当我们的Service须要为其它应用提供服务的时候,我们就要用到远程Service了。远程Service有两种实现方式,各自是Messenger方式与AIDL(Andriod进程间接口描写叙述语言)方式。以下分别进行介绍。

Messenger方式

在这样的方式中。我们定义一个Handler来处理不同的Message对象。

这个Handler是Messenger实现与client共享IBinder的基础,它同意client通过Message对象向Service发送命令。

另外,client也能够定义一个Messenger。这样,Service也能够把消息发送给client。  这是实现进程间通信的最简单的方式,由于Messenger队列将会在单线程中运行。我们不须要去考虑线程安全。

使用Messenger实现进程间通信的步骤:

l  实现一个Handler,它用来处理传递的Message对象;

l  创建一个Messenger对象,将Handler对象作为构造參数。

l  使用Messenger对象创建一个IBinder对象,并通过onBind返回;

l  client将接收到的IBinder对象作为Messenger的构造參数。实例化一个Messenger对象。这个Messenger对象将拥有Handler的引用;

l  在client通过Handler发送Message对象。Service中就能够通过Handler的handleMessage处理这个Message。

以下提供一个演示样例:

我们在ServiceTestproject下新建MessengerService类:

public class MessengerService extendsService {/*** Command to the service to display a message*/static final int MSG_SAY_HELLO = 1;static final int MSG_GET_CLIENT_MESSENGER = 2;static final int MSG_FROM_SERVICE = 3;private String TAG = getClass().getSimpleName();Messenger messengerToClient;/*** Handler of incoming messages from clients.*/class ServiceIncomingHandler extends Handler {@Overridepublic void handleMessage(Message msg) {Log.d(TAG, TAG + "------>handleMessage()");switch (msg.what) {case MSG_SAY_HELLO:Log.d(TAG,"handleMessage------>MSG_SAY_HELLO!");Toast.makeText(getApplicationContext(), "hello!",Toast.LENGTH_SHORT).show();break;case MSG_GET_CLIENT_MESSENGER:Log.d(TAG,"handleMessage------>Service收到Activity的messenger对象!");//此处获得可向client发送消息的Messenger对象messengerToClient =msg.replyTo;Message serviceMsg =Message.obtain(null, MSG_FROM_SERVICE, 0, 0);try {//向client发送消息messengerToClient.send(serviceMsg);} catch (RemoteException e){e.printStackTrace();}break;default:super.handleMessage(msg);}}}/*** 将这个serviceMessenger发送给client。client就能够通过它联系Service了*/final Messenger serviceMessenger = new Messenger(newServiceIncomingHandler());/*** When binding to the service, we return an interface to our messenger* for sending messages to the service.*/@Overridepublic IBinder onBind(Intent intent) {Log.d(TAG, TAG + "------>onBind()");Toast.makeText(getApplicationContext(), "binding",Toast.LENGTH_SHORT).show();return serviceMessenger.getBinder();}
}


新建MessengerTestproject,并创建ActivityMessenger类:

public classActivityMessenger extends Activity {/*** Messenger for communicating with theservice.*/Messenger messengerToService = null;/*** Flag indicating whether we have calledbind on the service.*/boolean mBound;private String TAG =getClass().getSimpleName();/*** Command to the service to display amessage*/static final int MSG_SAY_HELLO = 1;static final intMSG_SEND_MESSENGER_TO_SERVICE = 2;static final int MSG_FROM_SERVICE = 3;/*** Class for interacting with the maininterface of the service.*/private ServiceConnection mConnection = newServiceConnection() {public voidonServiceConnected(ComponentName className, IBinder service) {// This is called when theconnection with the service has been// established, giving us theobject we can use to// interact with the service.  We are communicating with the// service using a Messenger, sohere we get a client-side// representation of that from theraw IBinder object.Log.d(TAG, TAG +"------>onServiceConnected()");messengerToService = newMessenger(service);mBound = true;Message msg = Message.obtain(null,MSG_SEND_MESSENGER_TO_SERVICE, 0, 0);msg.replyTo = activityMessenger;try {//Messenger对象发送消息。这个msg对象将交给Service类中的handleMessage处理messengerToService.send(msg);} catch (RemoteException e) {e.printStackTrace();}}public voidonServiceDisconnected(ComponentName className) {// This is called when theconnection with the service has been// unexpectedly disconnected --that is, its process crashed.Log.d(TAG, TAG +"------>onServiceDisconnected()");messengerToService = null;mBound = false;}};/*** Handler of incoming messages fromservice.*/class IncomingHandler extends Handler {@Overridepublic void handleMessage(Message msg){Log.d(TAG, TAG + "------>handleMessage()");switch (msg.what) {case MSG_FROM_SERVICE:Log.d(TAG, TAG +"+MSG_FROM_SERVICE------>Activity收到Service回复的消息!");Toast.makeText(getApplicationContext(), "MSG_FROM_SERVICE!",Toast.LENGTH_SHORT).show();break;default:super.handleMessage(msg);}}}/*** Target we publish for service to sendmessages to IncomingHandler.*/final Messenger activityMessenger = newMessenger(new IncomingHandler());public void sayHello(View v) {Log.d(TAG, TAG +"------>sayHello()");if (!mBound) return;// Create and send a message to theservice, using a supported 'what' valueMessage msg = Message.obtain(null,MSG_SAY_HELLO, 0, 0);try {//Messenger对象发送消息,这个msg对象将交给Service类中的handleMessage处理messengerToService.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overrideprotected void onCreate(BundlesavedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, TAG +"------>onCreate()");}@Overrideprotected void onStart() {Log.d(TAG, TAG +"------>onStart()");super.onStart();// Bind to the servicebindService(newIntent("com.example.servicestudy.remoteservie.MessengerService"),mConnection,Context.BIND_AUTO_CREATE);}@Overrideprotected void onStop() {Log.d(TAG, TAG +"------>onStop()");super.onStop();// Unbind from the serviceif (mBound) {unbindService(mConnection);mBound = false;}}
}

Acitivty与Service都须要在Manifest文件里注冊。Service须要设置android:exported属性为true,并设置intent-filter。

例如以下所看到的:

<service    android:name=".remoteservie.MessengerService"android:enabled="true"android:exported="true"><intent-filter><actionandroid:name="com.example.servicestudy.remoteservie.MessengerService"/></intent-filter></service>

好了,编码工作我们已经完毕了,以下我们进行一下測试。打印日志例如以下:

03-1713:11:46.195  D/ActivityMessenger﹕ActivityMessenger------>onCreate()
03-1713:11:46.195  D/ActivityMessenger﹕ActivityMessenger------>onStart()
03-1713:11:46.215  D/MessengerService﹕MessengerService------>onBind()
03-17 13:11:46.235  D/ActivityMessenger﹕ActivityMessenger------>onServiceConnected()
03-1713:11:46.275  D/MessengerService﹕MessengerService------>handleMessage()
03-17 13:11:46.275 D/MessengerService﹕ handleMessage------>Service收到Activity的messenger对象!
03-17 13:11:46.285  D/ActivityMessenger﹕ActivityMessenger------>handleMessage()
03-17 13:11:46.285 D/ActivityMessenger﹕ ActivityMessenger+MSG_FROM_SERVICE------>Activity收到Service回复的消息!
03-1713:11:55.425  D/ActivityMessenger﹕ ActivityMessenger------>sayHello()
03-1713:11:55.425  D/MessengerService﹕MessengerService------>handleMessage()
03-1713:11:55.425  D/MessengerService﹕handleMessage------>MSG_SAY_HELLO!
03-1713:12:00.665  D/ActivityMessenger﹕ActivityMessenger------>onStop()

看见了吗?完美实现了Activity与Service互相发送消息。它们都是基于Messenger对象,Activity从Ibinder处获得一个可向Service发送消息的Messenger对象,接着Activity给Service发送了一个消息,并将可向Activity发送消息的Messenger对象携带过去。这样就实现了他们之间的交互。

逻辑上和便立即都非常easy易懂吧。点个赞。

好了,既然Messenger这么简单易用,为什么我们还须要继续看AIDL方式呢?不知道你有没有想过。Messenger方式在处理client发送的消息时,是将全部消息排成一个队列,然后依次处理。也就是单线程处理方式,这样的处理方式的长处是简便不easy引起其它问题,比方线程安全。可是。对于一些即时性要求比較高的服务,这样的方式可能就不够用了,或许我们须要採用多线程的方式,将接收到的请求尽快处理。这时候就能够直接使用AIDL方式了。

AIDL方式

事实上我悄悄告诉你,Messenger方式的底层实现也是基于AIDL方式实现的,系统为了方便跨进程的服务。为我们提供了一个Messenger类来便利的实现,可是它可能无法满足我们的需求,这时候我们就须要直接基于AIDL方式实现了。

事实上AIDL的实现不难,仅仅是有非常多细节须要注意。我这里也只是多描写叙述推荐一篇文章。有代码和总结:http://blog.csdn.net/songjinshi/article/details/22918405

我把总结摘抄过来:

AIDL的创建方法
AIDL语法非常easy,能够用来声明一个带一个或多个方法的接口,也能够传递參数和返回值。因为远程调用的须要, 这些參数和返回值并非不论什么类型.以下是些AIDL支持的数据类型
1. 不须要import声明的简单Java编程语言类型(int,boolean
2.String, CharSequence不须要特殊声明 
3.List, MapParcelables类型, 这些类型内所包括的数据成员也仅仅能是简单数据类型, String等其它比支持的类型. ( (另外: 我没尝试Parcelables, Eclipse+ADT下编译只是, 也许以后会有所支持). 
以下是AIDL语法
// 文件名称: SomeClass.aidl // 文件能够有凝视, java的一样 // package曾经的凝视, 将会被忽略. // 函数和变量曾经的凝视, 都会被增加到生产java代码中. package com.cmcc.demo; 
//import 引入语句 import com.cmcc.demo.ITaskCallback; 
interfaceITaskBinder { 
//函数跟java一样, 能够有0到多个參数 ,能够有一个返回值 boolean isTaskRunning(); 
voidstopRunningTask(); //參数能够是另外的一个aidl定义的接口 void registerCallback(ITaskCallback cb); 
voidunregisterCallback(ITaskCallback cb); 
//參数能够是String, 能够用in表入输入类型, out表示输出类型
intgetCustomerList(in String branch, out String customerList); 

实现接口时有几个原则
.抛出的异常不要返回给调用者. 跨进程抛异常处理是不可取的
.IPC调用是同步的

假设你知道一个IPC服务须要超过几毫秒的时间才干完毕地话,你应该避免在Activity的主线程中调用。也就是IPC调用会挂起应用程序导致界面失去响应. 这样的情况应该考虑单起一个线程来处理
.不能在AIDL接口中声明静态属性。

 
IPC的调用步骤
1. 声明一个接口类型的变量,该接口类型在.aidl文件里定义。 
2. 实现ServiceConnection 
3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递
4. ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service). 调用YourInterfaceName.Stub.asInterface((IBinder)service)将參数转换为YourInterface类型。 
5. 调用接口中定义的方法。

你总要检測到DeadObjectException异常。该异常在连接断开时被抛出。它仅仅会被远程方法抛出。 
6. 断开连接。调用接口实例中的ApplicationContext.unbindService() 

 

同一时候。我自己对AIDL实现也做了充分的測试,实现了Activity与Service之间互相调用方法,有兴趣的朋友能够下下来測试一下。

Github地址:https://github.com/BBigBoy/AndroidServiceFULLStudy

 

 

至此,关于Service的所有学习均已完毕。我们进行了Android Service的完整測试学习,主要包含生命周期測试,本地绑定执行Service实现、远程绑定执行Service的Messenger方式与AIDL方式实现 。  所有BOUND SERVICE的演示样例均实现了Service与client的交互功能。即Service能够调用client的方法,client也能够调用Service的方法。完整的项目见Github仓库:

https://github.com/BBigBoy/AndroidServiceFULLStudy

版权声明:本文博主原创文章,博客,未经同意不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hello Playwright:(3)基本概念

下面介绍一下 Playwright 中的基本概念&#xff1a;Headless 浏览器Playwright 需要特定版本的浏览器二进制文件才能运行。这些浏览器都支持 2 种 运行模式&#xff1a;Headless&#xff0c;无浏览器 UI&#xff0c;运行速度较快&#xff0c;常用于自动化运行Headed&#xff0c…

[python opencv 计算机视觉零基础到实战] 十二 直方图

一、学习目标 了解matplotlib绘图库的使用了解如何通过折线图或者直方图对图表进行绘制了解了通过图标对图片内容进行直观判断 如有错误欢迎指出~ 二、了解图像直方图及其应用 2.1 了解matplotlib库 在了解图像直方图前我们需要了解一个matplotlib库&#xff0c;matplotli…

建造者模式之项目运用

1 问题 建造者模式&#xff0c;我们也许不陌生&#xff0c;因为我们看到很多开源框架或者Android源码里面用到&#xff0c;类似这样的代码结构 A a new A.builder().method1("111").method2("222").build(); 很明显&#xff0c;一般这里的结构有builde…

专题三--1005

题目 A group of researchers are designing an experiment to test the IQ of a monkey. They will hang a banana at the roof of a building, and at the mean time, provide the monkey with some blocks. If the monkey is clever enough, it shall be able to reach the …

shell中的数字

shell中的数字 author :headsen chen date :2017-10-18 15:01:42 个人原创&#xff0c;转载请注明作者&#xff0c;出处&#xff0c;否则依法追究法律责任 1,生成随机数&#xff08;范围&#xff1a;0-32767&#xff09;&#xff0c;用特殊变量&#xff1a;RANDOM 2&#xff…

serviceloader java_【java编程】ServiceLoader使用看这一篇就够了

转载:https://www.jianshu.com/p/7601ba434ff4想必大家多多少少听过spi&#xff0c;具体的解释我就不多说了。但是它具体是怎么实现的呢&#xff1f;它的原理是什么呢&#xff1f;下面我就围绕这两个问题来解释&#xff1a;实现: 其实具体的实现类就是java.util.ServiceLoader…

.NET7 Preview4 之OpenAPI swagger改进

在MiniAPI系列中&#xff0c;《.NET6之MiniAPI(十八)&#xff1a;OpenAPI swagger》介绍了swagger在MiniAPI框架中的使用&#xff0c;当时留下很多不足&#xff0c;随着.NET7 Preview4的推出&#xff0c;这方面得到了很大的改进&#xff0c;我还是使用“十八”这篇文章的案例。…

Swift - 自定义单元格实现微信聊天界面

1&#xff0c;下面是一个放微信聊天界面的消息展示列表&#xff0c;实现的功能有&#xff1a; &#xff08;1&#xff09;消息可以是文本消息也可以是图片消息&#xff08;2&#xff09;消息背景为气泡状图片&#xff0c;同时消息气泡可根据内容自适应大小&#xff08;3&#x…

[python opencv 计算机视觉零基础到实战] 十三 直方图颜色提鲜

一、学习目标 了解了均衡化的作用是什么了解灰度、YUV、彩色图片均衡化的方法是使用什么方法了解了合并通道的方法是什么了解了分离通道的方法是什么 如有错误欢迎指出~ 二、了解图像均衡化 2.1 了解直方图均衡化 图像直方图均衡化主要是对图像中的少数灰度进行压缩&#…

java 中字符串比较方法_java中常用的字符串的比较方法(两种)

比较字符串比较常用的两个方法是运算符“”和String的equals方法。使用“”比较两个字符串&#xff0c;是比较两个对象的的“地址”是否一致&#xff0c;本质就是判断两个变量是否指向同一个对象&#xff0c;如果是则返回true&#xff0c;否则返回的是false。而String类的equal…

Android之稍微靠谱点的透明Activity(不获取触摸事件)

1 问题 实现透明的Activity(不获取触摸事件),就行什么也看不到,打开了透明activity,也不影响其他页面的滑动和点击,就行什么事情都没发生一样。 2 代码实现 1)配置样式 <style name="TestTheme" parent="Theme.AppCompat.Light"><item na…

分布式服务框架dubbo原理解析 转

alibaba有好几个分布式框架&#xff0c;主要有&#xff1a;进行远程调用(类似于RMI的这种远程调用)的(dubbo、hsf)&#xff0c;jms消息服务(napoli、notify)&#xff0c;KV数据库(tair)等。这个框架/工具/产品在实现的时候&#xff0c;都考虑到了容灾&#xff0c;扩展&#xff…

【倾情奉献】遥感物候研究:30年长时间序列遥感数据集GIMMS 3g NDVI产品预处理完整步骤

本文为作者硕士学位论文遥感物候研究数据处理过程总结。GIMMS(Global Inventory Modelling and Mapping Studies) 3g NDVI来源于ECOCAST网站(http://ecocast.arc.nasa.gov),是由NOAA卫星搭载的AVHRR传感器获取的全球植被数据,空间分辨率为0.0833 ,时间分辨率为15 d,一…

过早的给方法中 引用对象 设为 null 可被 GC提前回收吗?

经常在代码中看到有人将 null 赋值给引用类型&#xff0c;来达到让 GC 提前回收的目的&#xff0c;这样做真的有用吗&#xff1f;今天我们就来研究一下。为了方便讲解&#xff0c;来一段测试代码&#xff0c;提前将 test1null &#xff0c;然后调用 GC.Collect() 看看是否能提前…

[python opencv 计算机视觉零基础到实战] 十五 直方图反向投影

一、学习目标 了解了直方图反向投影的一般流程了解2D直方图的使用 如有错误欢迎指出~ 二、了解直方图反向投影 2.1 了解2D直方图 需要对直方图进行反向投影&#xff0c;需要使用2D直方图。2D直方图需要使用calcHist方法。calcHist方法在前两节中已经有了解&#xff0c;现在…

关联规则java代码_重量挖掘关联规则挖掘方法,哪个大神可以将以下伪代码转换为Java代码?...

重量挖掘关联规则挖掘方法&#xff0c;哪个大神可以将以下伪代码转换为Java代码&#xff1f; 10改进的加权关联规则算法的基本步骤与Apriori算法相似: 首先找到加权支持度不小于用户指定的最小加权支持度的所有频繁项集加权关联规则&#xff0c;然后使用频繁项集生成所有满足最…

Boostrap ZURB Foundation —— Web开发前端框架

webflow&#xff1a;Webflow 允许设计师通过自由的拖拉拽与 CSS 类互动&#xff0c;而定义它们的过程无需写任何一行代码。用户在完成从设计到 CSS 构架之后&#xff0c;甚至可以在线直接将建好的网页发布&#xff0c;而不需要导出代码到其他发布工具上。类似的这些 B2D 市场&a…

Git之HEAD和origin

1 问题 我们经常看见git相关操作里面看到HEAD和origin这些专业名称&#xff0c;它娘的到底什么意思。 2 解释 1&#xff09;HEAD git 中的分支&#xff0c;本质上仅仅是个指向 commit 对象的可变指针&#xff0c; HEAD 是一个特别指针&#xff0c;它是一个指向你正在工作中的…

如何离线安装chrome插件

如何离线安装chrome插件 本文转自Work Hard Work Smart博客园博客&#xff0c;原文链接&#xff1a;http://www.cnblogs.com/linlf03/p/6838852.html&#xff0c;如需转载请自行联系原作者