基于5.1代码Contacts模块拨号流程

之前的总结介绍过联系人界面的快速拨号流程以及显示界面的接收数据过程,现在着重讲中间是怎么从OutgoingBroadcast过来拨号界面,中间的联系人信息是如何传递的,以及传递的框架机制。本文通过bug“联系人中多个不同联系人相同号码拨号时如何显示正确姓名”来介绍。

一.Contacts中单击事件

1.Contacts中PeopleActivity中的DefaultContactBrowseListFragment,createListAdapter方法中定位到ContactsCommon模块的DefaultContactListAdapter中,看其父类ContactListAdapter--bindView方法又调用了bindQuickCallView方法(view是由本类的newView方法中调用父类ContactEntryListAdapter的newView方法中的ContactListItemView类实例来的),参考ContactsCommon模块中的ContactListItemView,其中初始化了快速拨号view--R.drawable.ic_action_call,在ContactListAdapter中注册了单击事件View.OnClickListenermClickListener;在监听事件中:

ContactsContract.CommonDataKinds.Phone.CONTENT_URI,

new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER,

ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY},

ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY + "=?",newString[] { lookup }, null);

需要增加ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME来获取姓名,再用intent传递出去;

2.在5.1代码上没有了快速拨号按钮,要进联系人信息中然后单击拨号才能打电话,所以在这里给值也发生了变化:

2.1.在QuickContactActivity中,显示电话号码等信息是由populateContactAndAboutCard方法中的(R.id.header):

if(contactCardEntries.size() > 0) {

mContactCard.initialize(contactCardEntries,

/* numInitialVisibleEntries =*/ MIN_NUM_CONTACT_ENTRIES_SHOWN,

/* isExpanded = */mContactCard.isExpanded(),

/* isAlwaysExpanded = */false,

mExpandingEntryCardViewListener,

mScroller);

mContactCard.setVisibility(View.VISIBLE);

} else {

mContactCard.setVisibility(View.GONE);

处理;

2.2.定位到ExpandingEntryCardView的initialize方法-->inflateInitialEntries-->createEntryView:

final TextView header =(TextView) view.findViewById(R.id.header);

if (!TextUtils.isEmpty(entry.getHeader())) {

header.setText(entry.getHeader());

} else {

header.setVisibility(View.GONE);

}

在这里赋值;

2.3.回到QuickContactActivity中,大标题中的姓名是在bindContactData方法中布局是mScroller(mScroller = (MultiShrinkScroller) findViewById(R.id.multiscroller)):

setHeaderNameText(ContactDisplayUtils.getDisplayName(this,data).toString());

在这里赋值;

修改显示正确姓名过程:

QuickContactsActivity.java(packages/apps/Contacts)-->mEntryClickHandler:

intent.putExtra("DIS_NAME",ContactDisplayUtils.getDisplayName(QuickContactActivity.this,mContactData).toString());//yyj

二.Sevice中接收Intent与分解过程

1.单击后跳转根据android.intent.action.CALL定位到/packages/service/Telephony中的./src/com/android/phone/OutgoingCallBroadcaster.java,进行intent的判断,最终会执行:

PhoneGlobals.getInstance().callController.placeCall(intent);

上面代码的作用是真正的拨号打电话发出信号过程,走到framework层完成拨号发送命令请求,最后在命令发送成功后会继续执行:

Intent broadcastIntent = newIntent(Intent.ACTION_NEW_OUTGOING_CALL);

这一句的作用在5.1/5.0上做了改动,4.4上面是直接使用startSipCallOptionHandler方法跳转到InCallUI去显示拨号界面然后finish,后面的broadcastIntent没有执行,而5.1/5.0中的startSipCallOptionHandler方法体中是空的,也就是说,直接走到broadcastIntent,发出了广播 ACTION_NEW_OUTGOING_CALL,这个广播可以很容易查出来是定位到了NewOutgoingCallIntentBroadcaster中(packages/services/Telecom)中,这个广播有何作用呢,查看此广播的注释或者processIntent方法注释:

/**

* OutgoingCallIntentBroadcasterreceives CALL and CALL_PRIVILEGED Intents, and broadcasts the

* ACTION_NEW_OUTGOING_CALL intent.ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which

* contains the phone number beingdialed. Applications can use this intent to (1) see which numbers

* are being dialed, (2) redirect acall (change the number being dialed), or (3) prevent a call

* from being placed.

*@return {@linkCallActivity#OUTGOING_CALL_SUCCEEDED} if the call succeeded, and an appropriate{@link DisconnectCause} if the call did not, describing why it failed.

这句话说明了如果拨号成功最后会来到此广播中(事实上就是上面的intent),由此广播链接到CallActivity(packages/services/Telecom)中,进入此类中可以查看到最后执行了:

intent.setClass(this, CallReceiver.class);

这行代码很明显,最后进入到了 CallReceiver中,

最后发出广播给packages/services/Telecomm/src/com/android/server/telecom/CallReceiver.java,然后调用processOutgoingCallIntent方法,在此方法中最终会调用:

Call call =getCallsManager().startOutgoingCall(handle, phoneAccountHandle, clientExtras);

2.因为传递的是handle,只包含电话号码,此时要想办法在调用startOutgoingCall方法之前加入联系人姓名信息,在这里注意到clientExtras,这个bundle是用来传递值的,正好利用其来put进联系人的姓名:

//yyj add begin--for display correct namein 2-3 the same number

if(intent.getStringExtra("DIS_NAME") != null &&!"".equals(intent.getStringExtra("DIS_NAME"))){

clientExtras.putString("DIS_NAME",intent.getStringExtra("DIS_NAME"));

}

//yyj add end

.CallsManager完成每路电话的控制管理

1.根据getCallsManager()可以定位到packages/services/Telecomm模块下的CallsManager.java中,进入startOutgoingCall方法,在这里构造了一个Call类的实例,这个Call是packages/services/Telecomm模块下的Call.java类,不过在这里:call.setExtras(extras),对传递进来的 clientExtras进行了一次保存,所以这个call可以暂时不用管,继续在最后调用addCall(call)方法;

进入addCall()方法,在这里,对call进行了保存,保存在一个列表mCalls 中,以待后用,继续:

private void addCall(Call call) {

Log.v(this,"addCall(%s)", call);

call.addListener(this);

mCalls.add(call);

// TODO:Update mForegroundCall prior to invoking

// onCallAddedfor calls which immediately take the foreground (like the first call).

for(CallsManagerListener listener : mListeners) {

listener.onCallAdded(call);

}

updateCallsManagerState();

}

这里 listener是CallsManagerListener类型的,是从mListeners列表中遍历出来的,构造方法中mListeners.add(mInCallController),这个 mInCallController是InCallController类型的,InCallController最终继承了CallsManagerListener,也就是说listener.onCallAdded(call)最终调用了InCallController中实现的 onCallAdded方法来保存call实例;

2.进入packages/services/Telecomm模块下的InCallController.java类中查看是否有onCallAdded方法,果然有此方法的实现:

public void onCallAdded(Call call) {

if(mInCallServices.isEmpty()) {

bind();

} else {

Log.i(this, "onCallAdded: %s", call);

// Track the call if we don't already know about it.

addCall(call);

for (Map.Entry<ComponentName, IInCallService> entry :mInCallServices.entrySet()) {

ComponentName componentName = entry.getKey();

IInCallService inCallService = entry.getValue();

ParcelableCall parcelableCall = toParcelableCall(call,

componentName.equals(mInCallComponentName) /* includeVideoProvider */);

try {

inCallService.addCall(parcelableCall);

} catch (RemoteException ignored) {

}

}}}

在此方法中注意,第一次拨号的时候会调用bind(),看条件mInCallServices.isEmpty(),其实mInCallServices是一个保存IInCallService实例的Map,当这个Map是空的时候就执行bind方法,先进bind方法在这里会进行一些判断和值的保存的工作,继续,很显然,在这里用到了Binder机制,当然也就用到了aidl来绑定service,在bind调用结束后会调用onConnected方法,进入onConnected方法:

private voidonConnected(ComponentName componentName, IBinder service) {

ThreadUtil.checkOnMainThread();

Log.i(this,"onConnected to %s", componentName);

IInCallServiceinCallService = IInCallService.Stub.asInterface(service);

try {

inCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),

mCallIdMapper));

mInCallServices.put(componentName, inCallService);

} catch(RemoteException e) {

Log.e(this, e, "Failed to set the in-call adapter.");

return;

}

// Uponsuccessful connection, send the state of the world to the service.

ImmutableCollection<Call>calls = CallsManager.getInstance().getCalls();

if(!calls.isEmpty()) {

Log.i(this, "Adding %s calls to InCallService after onConnected: %s",calls.size(),componentName);

for (Call call : calls) {

try {// Track the call if we don't already know about it.

Log.i(this, "addCall after binding: %s",call);

addCall(call);

inCallService.addCall(toParcelableCall(call,

componentName.equals(mInCallComponentName) /* includeVideoProvider */));

} catch (RemoteException ignored) {

}}

onAudioStateChanged(null, CallsManager.getInstance().getAudioState());

} else {

unbind();

}}

在这里看到 calls,其实是从CallsManager中拿出来的,回看就会看到addCall方法中已经进行过保存了,拿出刚才的call实例,调用:

nCallService.addCall(toParcelableCall(call,componentName.equals(mInCallComponentName));

先看 toParcelableCall方法,是将Call实例转换成了ParcelableCall实例,有什么意义呢,当然有,ParcelableCall继承了Parcelable,持久化数据,这样的话,Call才能被转换成可以使用Handler传递的对象数据,在这里要加上自己的联系人姓名字段并且给值,这样的话,数据就传递到了下一层(framework层);

四.数据通过IInCallService进入到framework完成转换处理

1.上面执行了什么呢?nCallService是什么,看代码,最后发现:

IInCallService inCallService =IInCallService.Stub.asInterface(service);

呵呵,aidl中的IInCallService实例,看上面代码,mInCallServices.put(componentName, inCallService),发现,只要是有继承了这个aidl的类就会被绑定,就保存其实例到列表mInCallServices,最后查明进入到了frameworks/base/telecomm的InCallService.java类中;

2.进入frameworks/base/telecomm的InCallService.java类,恍然大悟,InCallServiceBinder,即被绑定到InCallController中的连接,看看,确实继承了IInCallService.Stub,并且实现了 addCall方法,进入此方法,调用了mHandler,看mHandler实例:

case MSG_SET_IN_CALL_ADAPTER:

mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));

onPhoneCreated(mPhone);

break;

case MSG_ADD_CALL:

mPhone.internalAddCall((ParcelableCall) msg.obj);

break;

在此case中,会先调用第一个,看不懂没关系,先看onPhoneCreated(mPhone),这个最终会调用子类中的方法,其实是InCallServiceImpl(packages/apps/InCallUI)中的onPhoneCreated,先看:

@Override

public voidonPhoneCreated(Phone phone) {

Log.v(this,"onPhoneCreated");

CallList.getInstance().setPhone(phone);

AudioModeProvider.getInstance().setPhone(phone);

TelecomAdapter.getInstance().setPhone(phone);

InCallPresenter.getInstance().setPhone(phone);

InCallPresenter.getInstance().setUp(

getApplicationContext(),

CallList.getInstance(),

AudioModeProvider.getInstance());

TelecomAdapter.getInstance().setContext(InCallServiceImpl.this);

}

这个方法中会调用CallList.getInstance().setPhone(phone),先不看别的,进入到CallList(packages/apps/InCallUI)中的 setPhone:

public void setPhone(Phone phone) {

mPhone =phone;

mPhone.addListener(mPhoneListener);

}

看 mPhoneListener:

private Phone.Listener mPhoneListener =new Phone.Listener() {

@Override

public voidonCallAdded(Phone phone, android.telecom.Call telecommCall) {

Call call = new Call(telecommCall);

Log.d(this, "onCallAdded: telecommCall name is:" + call.getState());

if (call.getState() == Call.State.INCOMING ||

call.getState() == Call.State.CALL_WAITING) {

onIncoming(call, call.getCannedSmsResponses());

} else {

onUpdate(call);

}}

@Override

public voidonCallRemoved(Phone phone, android.telecom.Call telecommCall) {

if (mCallByTelecommCall.containsKey(telecommCall)) {

Call call = mCallByTelecommCall.get(telecommCall);

call.setState(Call.State.DISCONNECTED);

call.setDisconnectCause(newDisconnectCause(DisconnectCause.UNKNOWN));

if (updateCallInMap(call)) {

Log.w(this, "Removing call not previouslydisconnected " + call.getId());

}

updateCallTextMap(call, null);

}

}

};

主要是onUpdate(call)方法,在此方法追踪下去,这里给 mPhone注册了 mPhoneListener不会马上调用,继续回到上一层InCallService的case中,继续,会调用:

mPhone.internalAddCall((ParcelableCall)msg.obj);//在此方法中会将姓名信息保存到call中:

//yyj add begin--for display correct namein 2-3 the same number

if(parcelableCall.getInCallUiDisName() != null && !"".equals(parcelableCall.getInCallUiDisName())){

call.setDisNameBySameNo(parcelableCall.getInCallUiDisName());

}

//yyj add end

在这里追踪Phone(frameworks\base\telecomm\java\android\telecom)类,可以看到最终会调用到:

private void fireCallAdded(Call call) {

for (Listenerlistener : mListeners) {

listener.onCallAdded(this, call);

}}

追查即知,调用的就是 mPhoneListener的 onCallAdded方法,在这里最终会在CallList的mCallById列表中将此Call加入进去,以便CallCardFragment调用显示到拨号界面,注意,这里的参数call是类型 android.telecom.Call telecommCall,继而调用newCall(telecommCall)进行了转换,转换成了InCallUI模块中的Call类以便调用;

3.让我们来看最后是如何拿到这个call来显示姓名的,在CallCardFragment(packages/apps/InCallUI)中

@Override

public voidonActivityCreated(Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

final CallListcalls = CallList.getInstance();

final Callcall = calls.getFirstCall();

getPresenter().init(getActivity(), call);

}

继而到了CallCardPresenter中的init中的startContactInfoSearch方法,接着onContactInfoComplete方法中。。最终调用updatePrimaryDisplayInfo,在这里:

//yyj add begin--for display correct namein 2-3 the same number

if(mPrimary.getTelecommCall().getDisNameBySameNo() != null&&!"".equals(mPrimary.getTelecommCall().getDisNameBySameNo())){

checkIdpName = mPrimary.getTelecommCall().getDisNameBySameNo();

}

//yyj add end

我拿出了传进来的call中的姓名信息,显示:

ui.setPrimary(number, checkIdpName,nameIsNumber, mPrimaryContactInfo.label,

mPrimaryContactInfo.photo, isConference,canManageConference,

mPrimaryContactInfo.isSipCall,isForwarded);

checkIdpName即是姓名信息,由setPrimary来更新拨号界面中的联系人信息。

五.剖析代码逻辑存在的问题与解决办法

1.经过上面一系列的讲解,大家可能会以为姓名显示这个功能最终实现了,事实上,并没有,由于android代码中各个层的访问权限不一样,在模块编译的情况下上面的代码没有问题,但是进行整编的时候,android中的脚本对类和类之间的访问做了很强的控制,导致上面的packages/apps层的类访问packages\services和frameworks\base出现问题,哪里出现问题了呢?具体是以下问题:

1.1.ParcelableCall.java(frameworks\base\telecomm\java\android\telecom):我们在此类中添加了字段用以保存姓名,但是最后在CallCardPresenter(packages\apps\incallui)中去显示调用的时候会发现 ParcelableCall已经被声明成了@hide,整编会报错,也就是说如果不在frameworks/base/api/system-current.txt中去声明是访问不到的,当然了被声明成@hide如果强制访问的话是可以的,但是会出现什么不能预期的问题就不得而知的,这是很危险的动作,所以尽量不要去做,但是在InCallController还是访问了这就有点搞不明白了,可能加了特殊控制,所以我们最后也还是要通过ParcelableCall去访问变量,但不是我们自己定义的getDisNameBySameNo等等,因为没有声明所以会出错,我们自己也不要再加,通过查看ParcelableCall类我们发现了一个字段mCallerDisplayName,这个字段在多次打log时发现永远是空的,也就是说没有被用到,而且通过看字面意思似乎也是为了保存姓名信息而存在的,这个时候我们就可以利用了,在InCallController.java(packages\services\telecomm\src\com\android\server\telecom)类中的toParcelableCall方法中我们可以这样:

if(call.getExtras().getString("DIS_NAME")!= null && !"".equals(call.getExtras().getString("DIS_NAME"))){//yyj

callerDisplayName =call.getExtras().getString("DIS_NAME");//yyj

}

其他的不用去管,这样mCallerDisplayName有值了,最后赋值给Call类中的字段即可;

1.2.Call.java(frameworks\base\telecomm\java\android\telecom):同样的,framework层的Call最后会被传入到apps层,之前我们做过一个动作,就是在Phone中给Call赋值以便传入的时候能够拿出姓名信息:call.setDisNameBySameNo(parcelableCall.getInCallUiDisName());----这行代码其实是没有问题的,为什么呢,虽然Call也是被声明成了@hide,但是同一个包中是可以访问的,现在不需要了,因为我发现在Call中的内部类Details中也有一个字段mCallerDisplayName,似乎跟 ParcelableCall如出一辙,事实上确实如此,此字段可以接受 ParcelableCall传过来的值,这个不用手动去改,因为在Phone类的internalAddCall方法中最后调用了:

call.internalUpdate(parcelableCall, mCallByTelecomCallId);

这行代码实际上实例化call的同时会用 parcelableCall去实例化内部类Details,当然也包括给字段mCallerDisplayName赋值,恰好使用的是 ParcelableCall类的mCallerDisplayName,后者已经在上面有值了;

这样的话,整个衔接过程完成了而且省去了中间赋值的过程;

1.3.最后显示姓名信息的时候,依然是在CallCardPresenter.java(packages/apps/incallui)-->init()-->updatePrimaryDisplayInfo(),换了方式:

//yyj add begin--for display correct namein 2-3 the same number

if(mPrimary.getTelecommCall().getDetails().getCallerDisplayName()!= null

&&!"".equals(mPrimary.getTelecommCall().getDetails().getCallerDisplayName())){

name =mPrimary.getTelecommCall().getDetails().getCallerDisplayName();

}

//yyj add end

六.扮演重要角色类的工作原理

上面介绍了整个流程,在这里有必要介绍下Phone和 InCallController:

1.Phone.java(frameworks\base\telecomm\java\android\telecom):

新增加的Phone.java在(frameworks\base\telecomm\java\android\telecom)目录下,其功 能是“A unified virtual device providing a means of voice (and other) communicationon a device.”,即作为一个虚拟设备提供通信服务。其类型如下,

public final class Phone {}

它的方法的实现主要依赖3个辅助类,InCallAdapter、Listener、Call,即呼叫适配器、监听器、控制器;

对于Phone的初始化,我们可以通过其方法的调用关系找到,例如查找internalAddCall,我们就会发现调用者为InCallService,其中mPhone即为Phone的实例,

case MSG_ADD_CALL:

mPhone.internalAddCall((ParcelableCall)msg.obj);

在InCallService里,当收到MSG_SET_IN_CALL_ADAPTER消息时,会创建一个Phone实例,同时也顺带创建了一个InCallAdapter实例,

case MSG_SET_IN_CALL_ADAPTER:

mPhone = new Phone(newInCallAdapter((IInCallAdapter) msg.obj));

onPhoneCreated(mPhone);

break;

MSG_SET_IN_CALL_ADAPTER消息是setInCallAdapter发出的,它是在InCallService里实现的AIDL接口类的服务端方法,

private final class InCallServiceBinderextends IInCallService.Stub {

@Override

public voidsetInCallAdapter(IInCallAdapter inCallAdapter) {

mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER,inCallAdapter).sendToTarget();

}

根据我们对service的了解,它在service类创建时创建binder,在service的onbind方法里将binder实例传入,执行客户端的ServiceConnection的onServiceConnected方法,所以在客户端,onConnected被执行,之后就是客户端 InCallController里的setInCallAdapter被调用,再间接调用前面提到的服务端的setInCallAdapter。

private class InCallServiceConnectionimplements ServiceConnection {

/** {@inheritDoc} */

@Override public voidonServiceConnected(ComponentName name, IBinder service) {

Log.d(this, "onServiceConnected:%s", name);

onConnected(name, service);

}

private void onConnected(ComponentNamecomponentName, IBinder service) {

ThreadUtil.checkOnMainThread();

Log.i(this, "onConnectedto %s", componentName);

IInCallService inCallService= IInCallService.Stub.asInterface(service);

try {

inCallService.setInCallAdapter(newInCallAdapter(CallsManager.getInstance(),

mCallIdMapper));

mInCallServices.put(componentName, inCallService);

} catch (RemoteException e) {

Log.e(this, e,"Failed to set the in-call adapter.");

return;

}

所以可以看出,Phone的实例就是在InCallService服务启动过程中创建的。

2.InCallController.java(packages\services\telecomm\src\com\android\server\telecom):

上面提到的binder的客户端InCallController位于(packages\services\telecomm\src \com\android\server\telecom)目录,根据manifest文件,它运行在TelecomApp这个应用里,这是一个的5.0 新进程。又根据android:persistent="true"这个属性我们知道,TelecomApp是开机自启动的。

InCallController是在CallsManager的构造函数里创建的,CallsManager又是在TelecomApp的onCreate方法里面创建的,

public void onCreate() {

super.onCreate();

if (UserHandle.myUserId() == UserHandle.USER_OWNER){

// Note: This style of initializationmimics what will be performed once Telecom is

// moved

// to run in the system service. Theemphasis is on ensuring that initialization of all

// telecom classes happens in one placewithout relying on Singleton initialization.

mMissedCallNotifier = newMissedCallNotifier(this);

mPhoneAccountRegistrar = newPhoneAccountRegistrar(this);

mCallsManager =new CallsManager(this, mMissedCallNotifier, mPhoneAccountRegistrar);

CallsManager.initialize(mCallsManager);

mTelecomService = newTelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,

mCallsManager, this);

ServiceManager.addService(Context.TELECOM_SERVICE,mTelecomService);

// Start the BluetoothPhoneService

BluetoothPhoneService.start(this);}}

所以,TelecomApp进程的启动过程中,创建了InCallController和CallsManager两个实例,这两个类实例相互关联。

9.以上就是整个拨号联系人姓名如何传到拨号界面上的全部过程,可能比较复杂,涉及到的比较重要的类归纳如下:

Call.java(frameworks\base\telecomm\java\android\telecom)

ParcelableCall.java(frameworks\base\telecomm\java\android\telecom)

Phone.java(frameworks\base\telecomm\java\android\telecom)

InCallService(frameworks\base\telecomm\java\android\telecom)

InCallController.java(packages\services\telecomm\src\com\android\server\telecom)

CallReceiver.java(packages\services\telecomm\src\com\android\server\telecom)

CallsManager(packages\services\telecomm\src\com\android\server\telecom)

CallCardPresenter.java(packages\apps\incallui\src\com\android\incallui)

ContactListAdapter.java(packages\apps\contactscommon\src\com\android\contacts\common)

Android5.1中Contacts模块拨号加载联系人信息流程相关推荐

  1. linux优化网页加载过程,【zz】Linux起步过程中硬件模块的加载

    [zz]Linux起步过程中硬件模块的加载 [zz]Linux起步过程中硬件模块的加载 日期:2014-05-16 浏览次数:20354 次 [zz]Linux启动过程中硬件模块的加载 文章来源不详. ...

  2. Android中dex文件的加载与优化流程

    目录 1.dex文件分析...1 2.odex文件...2 2.1.odex文件结构...2 2.2.odex文件结构分析...3 3.dex文件的验证与优化...3 3.1 dex文件加载流程... ...

  3. ocx请确保该二进制存储在指定的路径中_模块*.ocx加载失败,请确保二进制存储在指定路径中...

    在调试项目时,创建控件时突然报这个错,这时对ocx这类文件不甚了解,先了解.ocx是什么文件.搜了资料了解到: OCX[1] 是对象类别扩充组件(Object Linking and Embeddin ...

  4. 模块加载及第三方包:Node.js模块化开发、系统模块、第三方模块、package.json文件、Node.js中模块的加载机制、开发环境与生产环境、cookie与session

    1.Node.js模块化开发 1.1 JavaScript开发弊端 JavaScript 在使用时存在两大问题,文件依赖和命名冲突. 1.2 软件中的模块化开发 一个功能就是一个模块,多个模块可以组成 ...

  5. 计算机进管理提示找不到入口,win10系统开机提示xxxdll模块已加载但找不到入口点的教程...

    有关win10系统开机提示xxxdll模块已加载但找不到入口点的操作方法想必大家有所耳闻.但是能够对win10系统开机提示xxxdll模块已加载但找不到入口点进行实际操作的人却不多.其实解决win10 ...

  6. dll侧加载_win7系统开机提示xxxdll模块已加载但找不到入口点的解决方法

    很多小伙伴都遇到过win7系统开机提示xxxdll模块已加载但找不到入口点的困惑吧,一些朋友看过网上零散的win7系统开机提示xxxdll模块已加载但找不到入口点的处理方法,并没有完完全全明白win7 ...

  7. res_config_mysql和chan_sip模块的加载分析

    1. res_config_mysql的模块加载早于chan_sip,他们的加载函数均为load_module 先分析res_config_mysql的load_module函数 parse_conf ...

  8. (16) Node.js 模块的加载逻辑

    一.按照组织方式划分模块 文件模块:是我们上一章节说过的,就是一个独立的.js文件. 目录模块:是我们可以将多个独立的.js文件统一存放在一个目录下,也就是放到一个文件夹中. 二.目录模块的加载逻辑 ...

  9. python加载机制_Python 模块的加载顺序

    基本概念 module 模块, 一个 py 文件或以其他文件形式存在的可被导入的就是一个模块 package 包,包含有 init 文件的文件夹 relative path 相对路径,相对于某个目录的 ...

最新文章

  1. commons-io_从Commons CLI迁移到picocli
  2. 初次使用VS附加到进程功能
  3. Nginx——事件驱动机制(雷霆追风问题,负载均衡)
  4. 日志服务客户端(logtail)异常检测工具
  5. DHT(Distributed Hash Table,分布式哈希表)
  6. qwtplot设置xy坐标轴原点重合_数控机床的原点、参考点以及坐标系怎么区分?不理解很容易混淆的...
  7. 力扣有没有java_力扣题解
  8. 饿了么是视障者非常喜欢的APP,你们要加油哦!
  9. 微信公众平台订阅号如何升级为服务号?
  10. 【自学Python:Day2】磨洋工的我一周一课……
  11. gee学习2数据获取、数据筛选、创建地理要素
  12. 叫谁修猫呢?叫蓝总|ONES 人物
  13. ZOC7-ssh工具配置快速登录执行命令
  14. 2020起重机械指挥模拟考试题库及起重机械指挥实操考试视频
  15. 【RHCE】NFS服务器简介及简单共享目录配置
  16. Job for network.service failed because the control process exited with error code. 的解决办法
  17. Top 100 大学
  18. PHYML32/10/15/20/30泡沫罐生产厂家品牌哪家好数据分析
  19. VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tupl
  20. 零基础HTML入门教程(17)——表单的单选框和复选框

热门文章

  1. struct timeval 和 struct timespec
  2. 浙江政务服务网用户体系对接(浙里办)
  3. 计算机配置中无可移动设备访问,由于您的计算机尚未建立以太网,wifi或移动数据连接,因此我们无法设置移动热点-...
  4. scanner java_Scanner在java中有什么用法怎么用
  5. ros路由器l5和l6的区别_深入对比分析海信L5L6L7d的区别???说说海信L5L6L7有什么不同呢?...
  6. C#运行出现:基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。
  7. iphone壁纸转换的解决
  8. 机器学习笔记之高斯混合模型(一)模型介绍
  9. Macos上的专业加密通讯工具,十分安全。使用了信号协议,该协议结合了预密钥,Double Ratchet算法和3-DH握手信号。
  10. 我与你,一同学Python(24)